Docs
IntegrationsGuides

Submitting a MassID

End-to-end guide for submitting a MassID — resolve participants, create the document, add events, and close.

Last updated on

Use this sequence as the base implementation pattern for submitting a complete MassID lifecycle through the Carrot API.

Prerequisites

Before you begin, ensure you have:

  • A registered Network Integrator account with valid API credentials
  • Participant and address data ready — resolve each one first (retrieve by key or create it) so you can reference it by participantId and addressId, as shown in Step 1
  • Familiarity with core concepts — especially the immutable event-sourced model

Step 1: Resolve participants and addresses

Documents and events reference participants and addresses by ID (some events, like CLOSE, only need a participant). Before submitting, resolve each one — look it up by key, or create it — and keep the returned participantId and addressId.

Retrieve by key. Look up an existing participant by its natural key — country code, tax ID type, and tax ID:

GET /participants?countryCode=BR&taxIdType=CNPJ&taxId=11111111111111

If it exists, reuse the returned participantId and list its addresses to find the addressId:

GET /participants/{participantId}/addresses

Create when missing. If the participant does not exist yet, create it, then add an address to it:

POST /participants
{
  "countryCode": "BR",
  "name": "Example Company",
  "taxId": "11111111111111",
  "taxIdType": "CNPJ",
  "type": "COMPANY"
}
POST /participants/{participantId}/addresses
{
  "name": "Main Facility",
  "street": "Rua das Colinas",
  "number": "500",
  "neighborhood": "Centro",
  "city": "São Paulo",
  "countryState": "São Paulo",
  "countryCode": "BR",
  "zipCode": "08575720",
  "latitude": -23.5489,
  "longitude": -46.6388
}

Each create call returns the generated id — use it as participantId / addressId in the next steps. Required fields: participant — countryCode, name, taxId, taxIdType, type; address — city, countryCode, countryState, name, number, street. See the Participants API for the full request and response schema.

Reusing existing records

Creating a participant or address that already exists is safe only if you send identical data — the platform matches on the natural key and rejects conflicting values. Prefer retrieving by key and reusing the returned ID.

Step 2: Create the document

Create the root record with classification and baseline visibility fields, referencing the participant and address you resolved in Step 1:

POST /v1/documents
{
  "category": "MassID",
  "type": "YOUR_WASTE_TYPE",
  "measurementUnit": "kg",
  "externalCreatedAt": "2026-03-01T10:00:00.000Z",
  "isPublic": true,
  "isPubliclySearchable": true,
  "participantId": "participant-id-from-step-1",
  "addressId": "address-id-from-step-1",
  "externalId": "your-internal-tracking-id",
  "deduplicationId": "unique-id-generated-before-first-attempt"
}
FieldRequiredDescription
categoryYesAlways MassID for mass tracking documents
typeYesWaste type — defined per methodology (see note below)
measurementUnitYeskg for recycling, kg CO₂e for carbon
externalCreatedAtYesISO 8601 timestamp of the real-world document creation
isPublicYesWhether the document is publicly visible
isPubliclySearchableYesWhether the document appears in public search
participantIdYesID of the participant resolved in Step 1
addressIdYesID of the address resolved in Step 1
externalIdNoYour internal ID for reconciliation — useful for mapping back to your system
deduplicationIdNoIdempotency key — see tip below

Deprecated: inline participant and address

You can still send full participant and address objects inline instead of participantId/addressId, and the platform will create them on first use. This inline form is deprecated and will be removed in a future release — resolve participants and addresses in Step 1 and reference them by ID instead.

Methodology-specific values

The type field (waste type) and measurementUnit are defined by your target methodology. See the methodology integration guides for the specific values required.

Reference: Documents API.

Step 3: Append timeline events

Append events in chronological order to represent operational steps. Each event is immutable once created.

ACTOR events

ACTOR events register participant roles on the document timeline. Each requires a label identifying the role, plus the participant and address it applies to, referenced by ID:

POST /v1/documents/{documentId}/events
{
  "name": "ACTOR",
  "label": "YOUR_ROLE_LABEL",
  "externalCreatedAt": "2026-03-01T10:05:00.000Z",
  "isPublic": true,
  "participantId": "actor-participant-id",
  "addressId": "actor-address-id"
}

The label value (e.g. "Waste Generator", "Hauler", "Processor") is defined by each methodology. See your methodology guide for the required roles and their labels.

Resolve each actor's participant and address the same way as in Step 1, then reference them by participantId and addressId.

CUSTOM events

CUSTOM events represent methodology-specific operational steps. The event name, required metadata attributes, and sequence are all defined by the methodology:

POST /v1/documents/{documentId}/events
{
  "name": "YOUR_EVENT_NAME",
  "externalCreatedAt": "2026-03-01T11:00:00.000Z",
  "isPublic": true,
  "participantId": "participant-id-from-step-1",
  "addressId": "address-id-from-step-1",
  "metadata": {
    "attributes": [
      { "name": "YOUR_ATTRIBUTE_NAME", "value": "attribute-value" }
    ]
  },
  "value": 150.5
}

The value field on events contributes to the document's currentValue. For example, a weighing event with value: 150.5 sets the tracked weight.

Deprecated: inline participant and address

Events accept the same deprecated inline participant and address objects as documents. Reference participantId and addressId instead — the inline form will be removed in a future release.

Methodology guides

Each methodology defines the specific event sequence, event names, and required metadata attributes. See the methodology integration guides for the values required by each methodology.

Document status lifecycle

Documents follow a strict status lifecycle:

  • OPEN — Default status after creation. Events can be appended freely.
  • CLOSE event — Transitions the document to CLOSED. After closing, only RELATED events are accepted.
  • CANCEL event — Transitions the document to CANCELLED. No further events are accepted.

Send a CLOSE event when the supply chain lifecycle is complete:

POST /v1/documents/{documentId}/events
{
  "name": "CLOSE",
  "externalCreatedAt": "2026-03-02T16:00:00.000Z",
  "isPublic": true,
  "participantId": "integrator-participant-id"
}

See Event Specification for the full list of event categories.

Reference: Events API.

For high-volume integrations, consider the batch events endpoint to submit multiple events per request.

Step 4: Attach evidence files (optional)

Some methodology rules require evidence attachments (e.g. scale tickets, transport manifests). The pattern is:

  1. Request a pre-signed upload URL via the Attachments API
  2. Upload the file directly to the pre-signed URL
  3. Reference the attachment in the relevant event's attachments array

Attachments are linked to specific events, not to the document as a whole. This ensures each piece of evidence is tied to the operational step it documents.

Reference: Attachments API.

Step 5: Retrieve and validate final state

Fetch the document and verify before considering the submission complete:

  • Event count and ordering — all expected events are present in chronological order
  • Status is CLOSED — the document has been properly closed
  • currentValue > 0 — the document has a positive tracked value
  • At least one ACTOR event — the required participant roles are registered
  • Participant and address links — all references resolve correctly
  • Metadata completeness — required attributes are present on each event per the methodology

Reference: GET document by ID.

Operational tips

deduplicationId

Always send deduplicationId on retryable write calls (document creation and event creation). Generate a unique ID before the first attempt, store it, and reuse the same ID on every retry. The ID is scoped per integrator — two different integrators can use the same ID without conflict. This guarantees at-most-once semantics: if the server received your first request but the response was lost, the retry will return the original result instead of creating a duplicate.

  • Use externalId on documents and events for reconciliation with your internal systems.
  • Treat 4xx as data/integration issues and 5xx as transient failures — see Error Handling.
  • Keep your source timestamp strategy deterministic — see Data Formats.

On this page