Skip to main content
POST
Create wastes: one, or up to 5,000 in a single request. The records are validated and stored at once, and their CO2e emissions are calculated asynchronously by a background worker. The response is an ingest job, not the wastes themselves: you poll the job to follow the calculation and read per-record outcomes.
Beta. This endpoint is in beta: the contract may still change before general availability. Pin your integration to the fields documented here and handle unknown fields in responses gracefully. Feedback is welcome through your Dcycle contact.

What is a waste

A waste is a quantity of residue that one of your facilities generates and hands over to a manager, who transports it to a treatment plant. Both steps emit greenhouse gases:
  • Treatment: landfilling, incineration, recycling, composting… Each treatment of each type of waste has its own emission factor.
  • Transport to the waste center: calculated from total_km_to_waste_center.
These emissions are reported under Scope 3, Category 5 (waste generated in operations) of the GHG Protocol. A waste record is characterized by:

How the emissions are calculated

Emissions are the quantity multiplied by an emission factor. What you send determines how Dcycle finds that factor. Only when you bring your own factor do you choose it.
  1. LER code (and R/D code when you have it): the standard route. The codes classify the waste in a common terminology, and Dcycle looks up the factor mapped to that LER + R/D pair in its emission factor databases: DEFRA by default, OCCC when DEFRA does not cover the code. Without an R/D code, only a factor defined for that LER with no treatment can be used, and not every LER has one, so send the R/D code whenever you have it.
  2. No codes, only a description: Dcycle compares the description with the factors of its maintained emission factor databases and selects the closest one for each year of the waste period. No LER or R/D code is assigned to the record. A low-confidence match is still applied, but the waste is flagged for review. Use it when your source data has no LER code.
  3. Your own factor, custom_ef_record_id: the only route where you choose the factor. The id of a custom emission factor record you created beforehand in a custom emission factor database of your organization, with wastes among its activity categories. Use it when you have a factor measured or provided by your waste manager. No LER code is needed; if you send one as well, your factor takes precedence. See Create Custom Record.
A record needs at least one of the three. If Dcycle cannot find a factor (the code pair is not covered, or no factor can be applied from the description), the waste is still stored and its calculation ends as CALCULATION_FAILED in the job’s items.
Sent with a description? The match can leave the waste waiting for a person: GET /v1/wastes/{waste_id}/detail shows status: "review" and LOW_CONFIDENCE_WASTE_FACTOR_MATCH in error_messages. The job item tells the two cases apart: succeeded when a factor was applied with low confidence (the waste has emissions, someone should confirm the factor), failed with CALCULATION_FAILED when no factor could be applied (no emissions until someone picks one). The job does not count these records separately yet.

Find the ids you send

A record does not carry your own codes: every reference is a Dcycle id (UUID). Look them up once and keep the mapping on your side; the catalogs rarely change. The ids of the LER, R/D and unit catalogs are the same in every Dcycle environment, so the ones in these examples work as they are.

How it works

  1. You send a list of records (records), one object per waste.
  2. All or nothing: every record is validated. If any record is invalid, the whole request is rejected with 422 BULK_RECORDS_REJECTED, the response lists every invalid record, and nothing is written. Fix them and resend the full batch.
  3. If every record is valid, all wastes are written in one transaction and the API answers 202 Accepted with the ingest job and a Location header pointing at it.
  4. A worker calculates the emissions in chunks of 100 records. Poll GET /v2/ingest-jobs/{job_id} until status is completed or completed_with_errors, then read the per-record outcomes at GET /v2/ingest-jobs/{job_id}/items.

One waste or many

The same request creates one waste or a whole batch. To create a single waste, send records with one element; it goes through the same validation, the same job and the same calculation as a batch. For more than 5,000 records, split them into several requests (see Limits).

Request

Headers

string
required
Your API key for authenticationExample: sk_live_1234567890abcdef
string
required
UUID of the organization the wastes are created for. Facilities of its accepted, enabled subsidiaries are also valid destinations.Example: a8315ef3-dd50-43f8-b7ce-d839e68d51fa
string
required
Must be application/json
string
required
1 to 255 characters. Send a unique value per batch (a UUID works well). If a request with the same key and the same body was already accepted for this organization, the API returns that same job instead of creating the wastes again, so a timeout or network error can be retried safely. The same key with a different body is rejected with 422 IDEMPOTENCY_KEY_REUSED: use a new key for a new batch.Example: c41d8e77-5d40-4a11-9e8b-2c9f4d1e7a03

Query Parameters

boolean
default:"false"
Validate only. Every check runs and an invalid batch answers the same 422, but nothing is written and no job is created: a valid batch answers 200 with {"dry_run": true, "submitted": n, "valid": n}. Use it to test an integration without creating data. The Idempotency-Key header is still required, but a dry run does not record it, so you can send the real request with the same key afterwards.

Body Parameters

array[object]
required
The wastes to create: between 1 and 5,000 objects. Each record has the fields below.

Response

202 Accepted

Every record passed validation and was stored. The body is the ingest job; the Location header holds its path.
uuid
Ingest job id. Use it with GET /v2/ingest-jobs/{job_id}.
string
processing right after the request. See Job status.
string
Always wastes for this endpoint.
string
Always create.
string
api_bulk when sent with an API key; app when sent by a person signed in to Dcycle (e.g. the app’s waste form).
object
integer
Number of calculation chunks (100 records each).
integer
Chunks already calculated. chunks_done / chunks_total is the job’s progress.
datetime
When the job was created (UTC).
datetime | null
When the last chunk finished (UTC), null while the job is processing.
self: the job. items: its per-record outcomes.

Job status

While a job is processing, the new wastes already exist with co2e at 0; the value is filled in when their chunk is calculated. Each record’s waste id is its entity_id in the job’s items.
If chunks_done stops moving, see A job that does not finish: never resend the batch with a new Idempotency-Key, because its wastes are already stored.

Why a record failed to calculate

A failed item carries error_code: CALCULATION_FAILED but not the reason yet (error_params is null). Read the waste with GET /v1/wastes/{waste_id}/detail:
  • status: "error": no emission factor covers its LER/R/D pair. A pair missing from GET /v1/wastes/rd-codes?ler_code_id=… never has one; a listed pair usually does.
  • status: "review": it came with a description that matched no factor. It has no emissions until someone picks the factor in Dcycle.

Errors

Errors follow RFC 9457 problem details (Content-Type: application/problem+json): a stable code, a human detail, errors when there is a list of problems, and the request_id to quote to support.

422 BULK_RECORDS_REJECTED — invalid records

At least one record is invalid. Nothing was written. rejected is the number of invalid records and errors lists them (up to 100) in submission order, each with its position in records (source_row_index, starting at 0), your client_row_id and a stable error_code.

422 IDEMPOTENCY_KEY_REUSED

The Idempotency-Key was already used in this organization with a different body. Nothing is written. Send a new key for a new batch; to retry the original batch, resend exactly the same body (field order does not matter).

422 REQUEST_VALIDATION_FAILED — malformed request

The request itself is invalid: the Idempotency-Key header is missing, or records is missing, empty or has more than 5,000 entries. errors lists each problem with where it is (loc):

401 Unauthorized / 403 Forbidden

Missing or invalid API key (401), or the API key’s user is not a member of the organization in x-organization-id (403 LOGGED_USER_NOT_MEMBER).

Examples

Create a batch and wait for the calculation

Split one waste across two facilities

This creates two wastes (60 and 40 units) sharing one source_waste_id. The job has one item for the record, whose entity_id is the first waste of the group.
Each share is a waste of its own: deleting that entity_id removes only the first share. If your integration may need to delete or replace the record later, send one record per facility instead of facility_percentages, so every waste has its own item and id.

Retries and idempotency

  • Network error or timeout: retry with the same Idempotency-Key. If the first request was accepted, you get the same job back and no wastes are duplicated.
  • 422 BULK_RECORDS_REJECTED: nothing was stored. Fix the records and send the batch again (a new key is fine, since the first request created nothing).
  • 422 IDEMPOTENCY_KEY_REUSED: the key already belongs to another batch. Use a new key, or resend the original body unchanged.

Limits

Get Ingest Job

Status and progress of the batch

List Ingest Job Items

Per-record outcomes, including calculation failures