REST API · v1

WasteBolt API

Create and manage Waste Transfer Notes programmatically. Integrate WasteBolt into your weighbridge software, ERP, or custom workflow.

4 Endpoints API Key Auth 60 req/min HTTPS only
Jump to section

Quick Start

The fastest way to explore the API is with our Bruno collection — a ready-to-run set of requests covering every endpoint, including hazardous waste, POPs, and DWT submission scenarios.

WasteBolt Bruno Collection

8 pre-built requests · all waste types · environment variables · DWT readiness examples

Download Collection
1

Install Bruno (free, open source API client)

2

Download and unzip the collection above, then open the folder in Bruno

3

Open Environments → Production and replace your_api_key_here with your key

4

Select the Production environment and run any request — check dwt_readiness in the response

Base URL

https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1

Authentication

All API requests require an x-api-key header containing your WasteBolt API key. Keys are generated from your account under Settings → Apps & Downloads. Keys are prefixed with wbsync_.

Example request header
x-api-key: wbsync_AbCdEfGhIjKlMnOpQrStUvWxYz012345

Keep your API key secret. Do not expose it in client-side code or public repositories. If a key is compromised, disable it immediately from Apps & Downloads and create a new one.

Rate Limiting

Each API key is limited to 60 requests per minute. Limits are tracked per key using a fixed 1-minute window. All responses include rate limit headers so you can monitor usage.

X-RateLimit-Limit

Max requests per window

60
X-RateLimit-Remaining

Requests left this window

47
X-RateLimit-Reset

Window reset timestamp (ISO)

2026-03-02T09:02:00Z

When the limit is exceeded you will receive a 429 response with a Retry-After header indicating seconds until reset.

429 Response
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2026-03-02T09:02:00.000Z
Retry-After: 23

{
  "success": false,
  "error": "Rate limit exceeded. 60 requests per minute allowed. Retry in 23s.",
  "code": "RATE_LIMIT_EXCEEDED"
}

Error Codes

All error responses share the same shape. The code field is stable and safe to use in your error handling logic.

Error response shape
{
  "success": false,
  "error": "Human readable message",
  "code": "MACHINE_READABLE_CODE"
}
CodeHTTPDescription
NO_API_KEY401x-api-key header missing from request
INVALID_KEY_FORMAT401Key does not start with wbsync_
INVALID_KEY401Key not found in WasteBolt
KEY_DISABLED401Key has been manually disabled
KEY_EXPIRED401Key has passed its expiry date
SUBSCRIPTION_INACTIVE403WasteBolt subscription has expired
RATE_LIMIT_EXCEEDED42960 req/min limit reached — see Retry-After header
VALIDATION_ERROR422Required field missing or invalid
INVALID_STATUS422Status value not allowed via API
NOT_FOUND404WTN not found or not accessible by this key
WTN_LOCKED409WTN is signed and cannot be updated
INVALID_JSON400Request body is not valid JSON
INVALID_ID400WTN id in the URL must be numeric
METHOD_NOT_ALLOWED405HTTP method not supported on this endpoint
NOTE_NUMBER_ERROR500Failed to generate a unique note number — retry
DATABASE_ERROR500Internal database error — retry after a moment
POST/create-wtn-api

Create a Waste Transfer Note

Creates a new WTN under your account. Returns the full note including the auto-generated note number.

Required Fields

producer_details.namestringName of the waste producer
producer_details.addressstringFull address of the producer
waste_details.waste_ewcstringEWC code e.g. "20 03 01" (asterisk for hazardous is fine)
waste_details.waste_namestringDescription of the waste
waste_details.weight_kgnumberWeight in kg — or set transfer_details.net_weight instead
transfer_details.transfer_datestringISO date e.g. "2026-03-02"

Optional Fields

carrier_detailsobjectCarrier name, address, vehicle_reg, license_no, registration_reason
consignee_detailsobjectReceiving site name, address, postcode, license_no, contact_email
note_typestring"single" (default) | "season_ticket"
statusstring"draft" (default) | "complete"

Hazardous waste — set waste_details.is_hazardous: true

These fields follow the same standard as WasteBolt's own hazardous WTN form, so notes created here submit to DWT identically.

waste_details.hazardous_property_codestring[]Required. One or more HP codes, e.g. ["HP_3","HP_14"]
waste_details.source_of_componentsstringRequired. NOT_PROVIDED | PROVIDED_WITH_WASTE | GUIDANCE | OWN_TESTING
waste_details.hazardous_componentsobject[]Required if source_of_components ≠ NOT_PROVIDED. [{ name, concentration }]
waste_details.hazardous_waste_consignment_codestringRecommended for DWT — omitting it just adds a readiness warning
waste_details.special_handling_requirementsstringOptional free text

POPs — set waste_details.contains_pops: true

POPs can apply to hazardous or non-hazardous waste — the two flags are independent.

waste_details.pops_source_of_componentsstringRequired. NOT_PROVIDED | PROVIDED_WITH_WASTE | GUIDANCE | OWN_TESTING
waste_details.pops_componentsobject[]Required if source ≠ NOT_PROVIDED. [{ code, concentration }] — code must be a valid POP code
curl -X POST https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1/create-wtn-api \
  -H "x-api-key: wbsync_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "producer_details": {
      "name": "Acme Waste Ltd",
      "address": "123 Industrial Estate, Birmingham, B1 1AA",
      "postcode": "B1 1AA"
    },
    "carrier_details": {
      "name": "Fast Carriers Ltd",
      "address": "456 Transport Road, Coventry, CV1 2BB",
      "vehicle_reg": "AB12 CDE",
      "license_no": "CBDU123456"
    },
    "consignee_details": {
      "name": "Green Recycling Ltd",
      "address": "789 Waste Park, Wolverhampton, WV1 3CC",
      "postcode": "WV1 3CC",
      "license_no": "EPR/AB1234CD/A001",
      "contact_email": "site@greenrecycling.co.uk"
    },
    "waste_details": {
      "waste_ewc": "20 03 01",
      "waste_name": "Mixed municipal waste",
      "weight_kg": 5000,
      "is_hazardous": false,
      "contains_pops": false
    },
    "transfer_details": {
      "transfer_date": "2026-03-02"
    },
    "note_type": "single",
    "status": "draft"
  }'
curl -X POST https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1/create-wtn-api \
  -H "x-api-key: wbsync_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "producer_details": {
      "name": "Acme Waste Ltd",
      "address": "123 Industrial Estate, Birmingham, B1 1AA"
    },
    "carrier_details": {
      "name": "Fast Carriers Ltd",
      "vehicle_reg": "AB12 CDE",
      "license_no": "CBDU123456"
    },
    "consignee_details": {
      "name": "Green Recycling Ltd",
      "address": "789 Waste Park, Wolverhampton",
      "postcode": "WV1 3CC",
      "license_no": "EPR/AB1234CD/A001",
      "contact_email": "site@greenrecycling.co.uk"
    },
    "waste_details": {
      "waste_ewc": "16 06 01*",
      "waste_name": "Waste batteries",
      "weight_kg": 500,
      "is_hazardous": true,
      "hazardous_property_code": ["HP_3", "HP_14"],
      "source_of_components": "OWN_TESTING",
      "hazardous_components": [
        { "name": "Lead", "concentration": 12.5 }
      ],
      "hazardous_waste_consignment_code": "ABC123/00001",
      "contains_pops": true,
      "pops_source_of_components": "OWN_TESTING",
      "pops_components": [
        { "code": "PFOS", "concentration": 0.005 }
      ]
    },
    "transfer_details": {
      "transfer_date": "2026-03-02"
    }
  }'
const response = await fetch(
  'https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1/create-wtn-api',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'wbsync_YOUR_KEY_HERE',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      producer_details: {
        name: 'Acme Waste Ltd',
        address: '123 Industrial Estate, Birmingham, B1 1AA',
      },
      waste_details: {
        waste_ewc: '20 03 01',
        waste_name: 'Mixed municipal waste',
        weight_kg: 5000,
      },
      transfer_details: {
        transfer_date: '2026-03-02',
      },
    }),
  }
);
const data = await response.json();
console.log(data.data.note_number); // e.g. "ACM-1042"
console.log(data.dwt_readiness);    // { ready, errors: [...], warnings: [...] }
201 Response
{
  "success": true,
  "message": "Waste Transfer Note created successfully",
  "data": {
    "id": 1042,
    "note_number": "ACM-1042",
    "status": "draft",
    "note_type": "single",
    "created_at": "2026-03-02T09:15:32.000Z",
    "producer_details": { ... },
    "carrier_details": { ... },
    "consignee_details": { ... },
    "waste_details": { ... },
    "transfer_details": { ... }
  },
  "dwt_readiness": {
    "ready": false,
    "errors": [],
    "warnings": [
      "Carrier registration/licence number is missing — DWT submission will need carrier_details.registration_reason"
    ]
  }
}

dwt_readiness

Every successful create response includes a dwt_readiness object — the same checks WasteBolt runs before a WTN can be submitted to DEFRA's Digital Waste Tracking service. errors block DWT submission until fixed; warnings won't block submission but will prompt for a reason (e.g. no carrier registration, no consignment code) inside the app.

GET/wtn-api

List Waste Transfer Notes

Returns a paginated list of WTNs for your account, newest first. Supports filtering by status, date range, note type, DWT status, and note number search.

Query Parameters

statusstringFilter by status: draft | complete | signed
note_typestringFilter: standard | hazardous | season_ticket
dwt_statusstringFilter: pending | submitted | failed
fromdateStart date (inclusive) e.g. 2026-01-01
todateEnd date (inclusive) e.g. 2026-12-31
searchstringSearch note_number (partial match)
pageintegerPage number, default 1
limitintegerResults per page, default 20, max 100
cURL
curl "https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1/wtn-api?status=complete&from=2026-01-01&limit=20&page=1" \
  -H "x-api-key: wbsync_YOUR_KEY_HERE"
200 Response
{
  "success": true,
  "data": [ { ... }, { ... } ],
  "pagination": {
    "total": 84,
    "page": 1,
    "limit": 20,
    "total_pages": 5,
    "has_next": true,
    "has_prev": false
  },
  "filters_applied": {
    "status": "complete",
    "from": "2026-01-01"
  }
}
GET/wtn-api/:id

Get a Single WTN

Returns a single WTN by its numeric ID. Only returns WTNs belonging to your account.

idintegerThe numeric WTN ID returned when creating or listing WTNs
cURL
curl "https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1/wtn-api/1042" \
  -H "x-api-key: wbsync_YOUR_KEY_HERE"
200 Response
{
  "success": true,
  "data": {
    "id": 1042,
    "note_number": "ACM-1042",
    "status": "complete",
    "note_type": "single",
    "created_at": "2026-03-02T09:15:32.000Z",
    "dwt_status": "pending",
    "producer_details": { ... },
    "carrier_details": { ... },
    "consignee_details": { ... },
    "waste_details": { ... },
    "transfer_details": { ... }
  }
}
PATCH/wtn-api/:id

Update WTN Status

Updates the status of a WTN. Signed WTNs are locked and cannot be modified via the API.

Body Fields

statusstring"draft" | "complete" — signed WTNs return 409 WTN_LOCKED
cURL
curl -X PATCH "https://vfhjhirnyulkvkmukpbj.supabase.co/functions/v1/wtn-api/1042" \
  -H "x-api-key: wbsync_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"status": "complete"}'
200 Response
{
  "success": true,
  "message": "WTN updated successfully",
  "data": {
    "id": 1042,
    "note_number": "ACM-1042",
    "status": "complete",
    ...
  }
}

API FAQ.

Common questions about integrating with WasteBolt.

How many requests can I make?

Each API key is limited to 60 requests per minute, tracked with a fixed 1-minute window. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers so you can monitor usage — exceeding the limit returns a 429 with a Retry-After header.

Can I create hazardous waste WTNs through the API?

Yes. Set waste_details.is_hazardous to true and supply the HP codes, source of components, and hazardous components — the same fields WasteBolt's own hazardous WTN form uses, so notes created via the API submit to DWT identically. POPs fields work the same way and are independent of the hazardous flag.

What happens if I try to update a signed WTN?

Signed WTNs are locked. A PATCH request against one returns a 409 with code WTN_LOCKED rather than modifying it.

Does the API support Season Tickets?

Yes — set note_type to "season_ticket" on create, the same as creating one in the web app.

What should I do if my API key is compromised?

Disable it immediately from Settings → Apps & Downloads in your WasteBolt account and generate a new one. Keys are prefixed wbsync_ and should never be exposed in client-side code or public repositories.

Ready to integrate?

Sign in to your WasteBolt account, head to Settings → Apps & Downloads, and generate your first API key in seconds.