ContentsIntroduction

Preview v1

API reference

Every endpoint, with its parameters, a sample in eight languages and the sandbox's real answer. Press Try it to send the request yourself.

Sandbox keymk_test_sbx_Ridgeview7Hq2Lk4Wd9
Open the consoleDownload OpenAPI 3.1

Introduction

The Mercovi API is organized around REST. It has resource URLs, takes and returns JSON, and uses standard HTTP methods, status codes and bearer authentication.

The API is in preview. The sandbox answers every endpoint on this page with sample data and keeps nothing you send. Production keys are set up with each customer during onboarding — book a call to start.

Times are ISO 8601 in UTC. Dates without a time are YYYY-MM-DD. Money is an integer in cents, with a lowercase currency. IDs are strings with a prefix that names the object, for example job_ or man_.

Base URLs
Sandbox      https://mercovi.com/api/sandbox/v1
Production   issued with your keys

Authentication

Send your key in the Authorization header as a bearer token on every request, over HTTPS. Requests without a key, or with one Mercovi didn't issue, get a 401.

Sandbox keys begin mk_test_ and live keys begin mk_live_. The sandbox key on these pages is public on purpose: it only opens sample data. Live keys are secrets. Keep them on your server, never in a browser or a mobile app, and revoke any key you think has leaked.

Each live key carries scopes, and an endpoint answers 403 if the key lacks the one it needs. The scopes are clients:read, clients:write, sites:read, sites:write, profiles:read, profiles:write, jobs:read, jobs:write, containers:read, containers:write, manifests:read, manifests:write, invoices:read, webhooks:read, webhooks:write, events:read.

Authenticated request
curl https://mercovi.com/api/sandbox/v1/clients \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"

Errors

Mercovi returns 2xx when a request worked, 4xx when something in the request needs to change, and 5xx when something failed on our side. Every error has the same JSON shape: type, a machine-readable code, a message written for the developer reading the log, the param at fault when there is one, and the request_id — quote it when you write to us.

StatusCodeWhen
400parameter_missingA required field is missing. param names it.
400parameter_invalidA field has the wrong type or a value outside its list.
401api_key_missingNo Authorization: Bearer header.
401api_key_invalidThe key isn't one Mercovi issued, or it was revoked.
403scope_missingThe key is real but lacks the scope this endpoint needs.
404resource_missingNo record with that ID in your account.
405method_not_allowedThe path exists but not with that method.
409idempotency_conflictAn Idempotency-Key was reused with a different body.
422state_invalidThe record can't make that change from where it is — for example, cancelling a closed job.
429rate_limitedToo many requests. Wait for the time in Retry-After.
500api_errorSomething failed on Mercovi's side. Safe to retry with the same Idempotency-Key.
Error response
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_missing",
    "message": "`kind` is required.",
    "param": "kind",
    "doc_url": "/developers/reference#errors",
    "request_id": "req_7Gx2Sample0001"
  }
}

Pagination

List endpoints return a page of records, newest first unless the endpoint says otherwise, with has_more and next_cursor. Ask for the next page by passing next_cursor as starting_after. limit sets the page size, from 1 to 100; the default is 25.

Cursors are record IDs, so pages stay stable when records are added while you read.

List response
{
  "object": "list",
  "data": [
    "…"
  ],
  "has_more": true,
  "next_cursor": "job_6Cp4HaldenNov"
}

Idempotency

Networks fail. To retry a POST without creating the same record twice, send an Idempotency-Key header with a value unique to that operation — a UUID works. A retry with the same key and the same body returns the first result. The same key with a different body returns a 409.

GET, PATCH and DELETE are idempotent already and don't need the header.

Retry-safe create
curl -X POST https://mercovi.com/api/sandbox/v1/clients \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: 5c0a4e9e-1d3b-4a51-9f0e-2b7f3c6d8a10" \
  -H "Content-Type: application/json" \
  -d '{"name": "Fairhaven Township", "kind": "hhw_program"}'

Versioning

The version is in the path: /v1. Within a version, Mercovi only makes additive changes — new endpoints, new optional fields, new event types, new values in a list. Write your client to ignore fields it doesn't know.

A change that would break a working client — removing or renaming a field, changing its type — ships as a new version, announced to key holders first, with the old one kept running alongside.

While the API is in preview, fields may still change inside v1. Key holders hear about each change before it ships.

Rate limits

Every response carries RateLimit-Policy and, in production, RateLimit-Remaining and RateLimit-Reset, so a client can slow down before it's stopped. A request over the limit gets a 429 with a Retry-After header in seconds.

Limits are set per account. If an integration needs more — a nightly sync of a large history, say — tell us and we'll set the limit with you.

Webhooks

Register an HTTPS endpoint and Mercovi sends it an event object whenever one of the events you chose happens. Answer with any 2xx within a few seconds and do the work afterwards. Deliveries that fail are retried with growing gaps between attempts. If your endpoint was down, list /v1/events to catch up.

Each delivery carries a Mercovi-Signature header: t=<unix time>,v1=<signature>, where the signature is an HMAC-SHA256 of <t>.<raw body> keyed by the endpoint's whsec_ secret. Compute it yourself, compare in constant time, and reject deliveries whose t is more than five minutes old.

EventWhen it's sent
job.createdA job — an HHW event, a C&I pickup or a lab pack — is created.
job.scheduledA job gets its date and is staffed.
job.closedA job is closed out: containers counted, weights in.
staff.acceptedA crew member accepts an assignment on a job.
container.receivedA container is checked in at a facility.
container.storage_status_changedA container's storage status moves — for example from within the limit to approaching it.
manifest.ready_for_signatureA manifest is complete and waiting for the generator's signature.
manifest.signedA manifest is signed; its status, submission method and tracking number are on the record.
profile.approvedA waste profile is approved.
profile.expiringA waste profile is nearing its renewal date.
invoice.sentAn invoice is sent to the client.
invoice.paidAn invoice is paid, in the client portal or recorded by your team.
Verify a delivery (Node.js)
import crypto from "node:crypto";

export function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const age = Date.now() / 1000 - Number(parts.t);
  if (!(age >= 0 && age < 300)) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const given = Buffer.from(parts.v1 ?? "", "hex");
  const want = Buffer.from(expected, "hex");
  return given.length === want.length && crypto.timingSafeEqual(given, want);
}

Clients

The organizations you serve: an HHW program, a plant or institution you collect from, or a laboratory. Sites, profiles, jobs and invoices all hang off a client.

The client object · 10 attributes

The client object

  • idstringrequired

    Unique identifier for the client, beginning cli_.

  • objectstring

    Always client.

  • namestringrequired

    The client's name as it appears on documents.

  • kindenum

    Which line the client is served under. Drives the client portal's navigation.

    hhw_programcommercial_industriallaboratory

  • epa_idstring

    The client's EPA ID number, when it is the generator of record.

  • billing_emailstring

    Where invoices go.

  • portal_enabledboolean

    Whether the client can sign in to the client portal.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

  • updated_attimestamp

    When the record last changed, ISO 8601 in UTC.

The client object
{
  "id": "cli_2Rv8Ridgeview",
  "object": "client",
  "name": "Ridgeview County HHW program",
  "kind": "hhw_program",
  "epa_id": null,
  "billing_email": "hhw@ridgeview.example",
  "portal_enabled": true,
  "metadata": {
    "erp_id": "C-10233"
  },
  "created_at": "2026-03-02T14:00:00Z",
  "updated_at": "2026-09-14T14:00:00Z"
}

List clients

GET/v1/clients

Returns your clients, newest first.

Scope clients:read · Returns 200

Query parameters

  • kindenum

    Only clients of this kind.

    hhw_programcommercial_industriallaboratory

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/clients
curl "https://mercovi.com/api/sandbox/v1/clients?limit=3" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "cli_2Rv8Ridgeview",
      "object": "client",
      "name": "Ridgeview County HHW program",
      "kind": "hhw_program",
      "epa_id": null,
      "billing_email": "hhw@ridgeview.example",
      "portal_enabled": true,
      "metadata": {
        "erp_id": "C-10233"
      },
      "created_at": "2026-03-02T14:00:00Z",
      "updated_at": "2026-09-14T14:00:00Z"
    },
    {
      "id": "cli_7Hd3Halden",
      "object": "client",
      "name": "Halden Manufacturing",
      "kind": "commercial_industrial",
      "epa_id": "OHD987654321",
      "billing_email": "ap@halden.example",
      "portal_enabled": true,
      "metadata": {},
      "created_at": "2026-04-18T14:00:00Z",
      "updated_at": "2026-09-30T14:00:00Z"
    },
    {
      "id": "cli_4Vl6Vantage",
      "object": "client",
      "name": "Vantage Laboratories",
      "kind": "laboratory",
      "epa_id": "NJR000123456",
      "billing_email": "ehs@vantagelabs.example",
      "portal_enabled": false,
      "metadata": {},
      "created_at": "2026-05-07T14:00:00Z",
      "updated_at": "2026-08-21T14:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "cli_4Vl6Vantage"
}

Create a client

POST/v1/clients

Adds a client. Send an Idempotency-Key so a retried request can't create it twice.

Scope clients:write · Returns 201

Body

  • namestringrequired

    The client's name.

  • kindenumrequired

    Which line the client is served under.

    hhw_programcommercial_industriallaboratory

  • epa_idstring

    Twelve characters: two-letter state code, a letter, nine digits.

  • billing_emailstring

    Where invoices go.

  • portal_enabledboolean

    Invite the client to the client portal. Defaults to false.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

POST /v1/clients
curl -X POST "https://mercovi.com/api/sandbox/v1/clients" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Fairhaven Township",
    "kind": "hhw_program",
    "billing_email": "dpw@fairhaven.example",
    "portal_enabled": true,
    "metadata": {
      "erp_id": "C-20418"
    }
  }'
Response201
{
  "id": "cli_7Gx2Sample0001",
  "object": "client",
  "epa_id": null,
  "billing_email": "dpw@fairhaven.example",
  "portal_enabled": true,
  "metadata": {
    "erp_id": "C-20418"
  },
  "created_at": "2026-10-09T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z",
  "name": "Fairhaven Township",
  "kind": "hhw_program"
}

Retrieve a client

GET/v1/clients/{id}

Returns one client.

Scope clients:read · Returns 200

Path parameters

  • idstringrequired

    The client's ID, beginning cli_.

GET /v1/clients/cli_2Rv8Ridgeview
curl "https://mercovi.com/api/sandbox/v1/clients/cli_2Rv8Ridgeview" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "cli_2Rv8Ridgeview",
  "object": "client",
  "name": "Ridgeview County HHW program",
  "kind": "hhw_program",
  "epa_id": null,
  "billing_email": "hhw@ridgeview.example",
  "portal_enabled": true,
  "metadata": {
    "erp_id": "C-10233"
  },
  "created_at": "2026-03-02T14:00:00Z",
  "updated_at": "2026-09-14T14:00:00Z"
}

Update a client

PATCH/v1/clients/{id}

Changes the fields you send and leaves the rest alone.

Scope clients:write · Returns 200

Path parameters

  • idstringrequired

    The client's ID, beginning cli_.

Body

  • namestring

    The client's name.

  • billing_emailstring

    Where invoices go.

  • portal_enabledboolean

    Turn client portal access on or off.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

PATCH /v1/clients/cli_2Rv8Ridgeview
curl -X PATCH "https://mercovi.com/api/sandbox/v1/clients/cli_2Rv8Ridgeview" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Content-Type: application/json" \
  -d '{
    "billing_email": "hhw-billing@ridgeview.example"
  }'
Response200
{
  "id": "cli_2Rv8Ridgeview",
  "object": "client",
  "name": "Ridgeview County HHW program",
  "kind": "hhw_program",
  "epa_id": null,
  "billing_email": "hhw-billing@ridgeview.example",
  "portal_enabled": true,
  "metadata": {
    "erp_id": "C-10233"
  },
  "created_at": "2026-03-02T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z"
}

Sites

A place you collect from: an event location, a plant's loading dock, a lab's stockroom. A client can have many.

The site object · 9 attributes

The site object

  • idstringrequired

    Unique identifier for the site, beginning site_.

  • objectstring

    Always site.

  • clientstringrequired

    The client's ID.

  • namestringrequired

    What your crew calls the place.

  • addressobject

    line1, line2, city, state, postal_code.

  • epa_idstring

    The site's own EPA ID, when it has one.

  • access_notesstring

    Gate codes, dock hours, who to ask for.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

The site object
{
  "id": "site_4Kp1Fairgrounds",
  "object": "site",
  "client": "cli_2Rv8Ridgeview",
  "name": "Ridgeview County Fairgrounds — Lot C",
  "address": {
    "line1": "1200 Fair St",
    "line2": null,
    "city": "Ridgeview",
    "state": "NJ",
    "postal_code": "07401"
  },
  "epa_id": null,
  "access_notes": "Enter from Gate B.",
  "metadata": {},
  "created_at": "2026-03-02T14:00:00Z"
}

List sites

GET/v1/sites

Returns sites, newest first.

Scope sites:read · Returns 200

Query parameters

  • clientstring

    Only this client's sites.

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/sites
curl "https://mercovi.com/api/sandbox/v1/sites?client=cli_7Hd3Halden" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "site_8Tn5HaldenDock",
      "object": "site",
      "client": "cli_7Hd3Halden",
      "name": "Plant 1 — shipping dock",
      "address": {
        "line1": "40 Foundry Rd",
        "line2": null,
        "city": "Halden",
        "state": "OH",
        "postal_code": "44101"
      },
      "epa_id": "OHD987654321",
      "access_notes": "Dock 2. Hard hats past the yellow line.",
      "metadata": {},
      "created_at": "2026-04-18T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create a site

POST/v1/sites

Adds a site to a client.

Scope sites:write · Returns 201

Body

  • clientstringrequired

    The client's ID.

  • namestringrequired

    What your crew calls the place.

  • addressobjectrequired

    line1, city, state, postal_code are required.

  • access_notesstring

    Gate codes, dock hours, who to ask for.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

POST /v1/sites
curl -X POST "https://mercovi.com/api/sandbox/v1/sites" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "client": "cli_7Hd3Halden",
    "name": "Plant 2 — receiving dock",
    "address": {
      "line1": "41 Foundry Rd",
      "city": "Halden",
      "state": "OH",
      "postal_code": "44101"
    },
    "access_notes": "Dock 4. Ask for EHS at the guard house."
  }'
Response201
{
  "id": "site_7Gx2Sample0001",
  "object": "site",
  "epa_id": null,
  "access_notes": "Dock 4. Ask for EHS at the guard house.",
  "metadata": {},
  "created_at": "2026-10-09T14:00:00Z",
  "client": "cli_7Hd3Halden",
  "name": "Plant 2 — receiving dock",
  "address": {
    "line1": "41 Foundry Rd",
    "city": "Halden",
    "state": "OH",
    "postal_code": "44101"
  }
}

Retrieve a site

GET/v1/sites/{id}

Returns one site.

Scope sites:read · Returns 200

Path parameters

  • idstringrequired

    The site's ID, beginning site_.

GET /v1/sites/site_4Kp1Fairgrounds
curl "https://mercovi.com/api/sandbox/v1/sites/site_4Kp1Fairgrounds" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "site_4Kp1Fairgrounds",
  "object": "site",
  "client": "cli_2Rv8Ridgeview",
  "name": "Ridgeview County Fairgrounds — Lot C",
  "address": {
    "line1": "1200 Fair St",
    "line2": null,
    "city": "Ridgeview",
    "state": "NJ",
    "postal_code": "07401"
  },
  "epa_id": null,
  "access_notes": "Enter from Gate B.",
  "metadata": {},
  "created_at": "2026-03-02T14:00:00Z"
}

Update a site

PATCH/v1/sites/{id}

Changes the fields you send.

Scope sites:write · Returns 200

Path parameters

  • idstringrequired

    The site's ID, beginning site_.

Body

  • namestring

    What your crew calls the place.

  • access_notesstring

    Gate codes, dock hours, who to ask for.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

PATCH /v1/sites/site_4Kp1Fairgrounds
curl -X PATCH "https://mercovi.com/api/sandbox/v1/sites/site_4Kp1Fairgrounds" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Content-Type: application/json" \
  -d '{
    "access_notes": "Enter from Gate B. Traffic cones in the shed."
  }'
Response200
{
  "id": "site_4Kp1Fairgrounds",
  "object": "site",
  "client": "cli_2Rv8Ridgeview",
  "name": "Ridgeview County Fairgrounds — Lot C",
  "address": {
    "line1": "1200 Fair St",
    "line2": null,
    "city": "Ridgeview",
    "state": "NJ",
    "postal_code": "07401"
  },
  "epa_id": null,
  "access_notes": "Enter from Gate B. Traffic cones in the shed.",
  "metadata": {},
  "created_at": "2026-03-02T14:00:00Z"
}

Waste profiles

A characterized waste stream for a client: what it is, its waste codes and DOT description, and whether the receiving facility has approved it. Manifest lines are built from profiles.

The waste_profile object · 10 attributes

The waste_profile object

  • idstringrequired

    Unique identifier for the profile, beginning prof_.

  • objectstring

    Always waste_profile.

  • clientstringrequired

    The client's ID.

  • namestringrequired

    The stream's common name.

  • waste_codesarray

    Federal and state waste codes, for example D001, F003.

  • dot_descriptionstring

    The proper shipping name, hazard class, UN number and packing group.

  • statusenum

    Where the profile is in approval.

    draftsubmittedapprovedexpired

  • approved_bystring

    The receiving facility's name, once approved.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

  • updated_attimestamp

    When the record last changed, ISO 8601 in UTC.

The waste_profile object
{
  "id": "prof_9Wq2Paint",
  "object": "waste_profile",
  "client": "cli_2Rv8Ridgeview",
  "name": "Oil-based paint",
  "waste_codes": [
    "D001"
  ],
  "dot_description": "UN1263, Waste Paint, 3, PG II",
  "status": "approved",
  "approved_by": "Ashfield Chemical Recovery",
  "created_at": "2026-03-04T14:00:00Z",
  "updated_at": "2026-03-20T14:00:00Z"
}

List waste profiles

GET/v1/waste_profiles

Returns profiles, newest first.

Scope profiles:read · Returns 200

Query parameters

  • clientstring

    Only this client's profiles.

  • statusenum

    Only profiles in this status.

    draftsubmittedapprovedexpired

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/waste_profiles
curl "https://mercovi.com/api/sandbox/v1/waste_profiles?status=approved" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "prof_9Wq2Paint",
      "object": "waste_profile",
      "client": "cli_2Rv8Ridgeview",
      "name": "Oil-based paint",
      "waste_codes": [
        "D001"
      ],
      "dot_description": "UN1263, Waste Paint, 3, PG II",
      "status": "approved",
      "approved_by": "Ashfield Chemical Recovery",
      "created_at": "2026-03-04T14:00:00Z",
      "updated_at": "2026-03-20T14:00:00Z"
    },
    {
      "id": "prof_6Ox4Oxidizer",
      "object": "waste_profile",
      "client": "cli_4Vl6Vantage",
      "name": "Lab pack — oxidizers",
      "waste_codes": [
        "D001"
      ],
      "dot_description": "UN1479, Waste Oxidizing solid, n.o.s. (potassium permanganate), 5.1, PG II",
      "status": "approved",
      "approved_by": "Ashfield Chemical Recovery",
      "created_at": "2026-05-09T14:00:00Z",
      "updated_at": "2026-05-30T14:00:00Z"
    },
    {
      "id": "prof_1Ac7Batteries",
      "object": "waste_profile",
      "client": "cli_2Rv8Ridgeview",
      "name": "Lead-acid batteries",
      "waste_codes": [
        "D002",
        "D008"
      ],
      "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
      "status": "approved",
      "approved_by": "Ashfield Chemical Recovery",
      "created_at": "2026-03-04T14:00:00Z",
      "updated_at": "2026-03-20T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create a waste profile

POST/v1/waste_profiles

Starts a profile in draft. Waste codes are checked against the federal and state code library.

Scope profiles:write · Returns 201

Body

  • clientstringrequired

    The client's ID.

  • namestringrequired

    The stream's common name.

  • waste_codesarrayrequired

    One or more waste codes.

  • dot_descriptionstring

    The proper shipping name and hazard class.

POST /v1/waste_profiles
curl -X POST "https://mercovi.com/api/sandbox/v1/waste_profiles" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "client": "cli_7Hd3Halden",
    "name": "Spent parts-washer solvent",
    "waste_codes": [
      "D001",
      "F002"
    ],
    "dot_description": "UN1993, Waste Flammable liquid, n.o.s., 3, PG II"
  }'
Response201
{
  "id": "prof_7Gx2Sample0001",
  "object": "waste_profile",
  "dot_description": "UN1993, Waste Flammable liquid, n.o.s., 3, PG II",
  "status": "draft",
  "approved_by": null,
  "created_at": "2026-10-09T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z",
  "client": "cli_7Hd3Halden",
  "name": "Spent parts-washer solvent",
  "waste_codes": [
    "D001",
    "F002"
  ]
}

Retrieve a waste profile

GET/v1/waste_profiles/{id}

Returns one profile.

Scope profiles:read · Returns 200

Path parameters

  • idstringrequired

    The profile's ID, beginning prof_.

GET /v1/waste_profiles/prof_9Wq2Paint
curl "https://mercovi.com/api/sandbox/v1/waste_profiles/prof_9Wq2Paint" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "prof_9Wq2Paint",
  "object": "waste_profile",
  "client": "cli_2Rv8Ridgeview",
  "name": "Oil-based paint",
  "waste_codes": [
    "D001"
  ],
  "dot_description": "UN1263, Waste Paint, 3, PG II",
  "status": "approved",
  "approved_by": "Ashfield Chemical Recovery",
  "created_at": "2026-03-04T14:00:00Z",
  "updated_at": "2026-03-20T14:00:00Z"
}

Update a waste profile

PATCH/v1/waste_profiles/{id}

Changes a profile in draft. An approved profile is changed by creating a new one.

Scope profiles:write · Returns 200

Path parameters

  • idstringrequired

    The profile's ID, beginning prof_.

Body

  • namestring

    The stream's common name.

  • waste_codesarray

    Replaces the list.

  • dot_descriptionstring

    The proper shipping name and hazard class.

PATCH /v1/waste_profiles/prof_3Lb8Solvent
curl -X PATCH "https://mercovi.com/api/sandbox/v1/waste_profiles/prof_3Lb8Solvent" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Content-Type: application/json" \
  -d '{
    "waste_codes": [
      "D001",
      "F003",
      "F005"
    ]
  }'
Response200
{
  "id": "prof_3Lb8Solvent",
  "object": "waste_profile",
  "client": "cli_7Hd3Halden",
  "name": "Spent degreasing solvent",
  "waste_codes": [
    "D001",
    "F003",
    "F005"
  ],
  "dot_description": "UN1993, Waste Flammable liquid, n.o.s. (xylene, acetone), 3, PG II",
  "status": "submitted",
  "approved_by": null,
  "created_at": "2026-09-22T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z"
}

Jobs

One piece of work: an HHW collection event, a commercial pickup, or a lab pack. A job has a client, a site, a date, a crew and containers; closing it writes the bill.

The job object · 14 attributes

The job object

  • idstringrequired

    Unique identifier for the job, beginning job_.

  • objectstring

    Always job.

  • typeenumrequired

    What kind of work it is.

    hhw_eventci_pickuplab_pack

  • clientstringrequired

    The client's ID.

  • sitestringrequired

    The site's ID.

  • statusenum

    Where the job is.

    draftscheduledin_progressclosedbilledcancelled

  • scheduled_fordate

    The date of the work.

  • windowobject

    start and end, local time, HH:MM.

  • crewarray

    Assigned staff: name, role, accepted.

  • container_countinteger

    Containers on the job so far.

  • invoicestring

    The invoice ID, once the job is billed.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

  • updated_attimestamp

    When the record last changed, ISO 8601 in UTC.

The job object
{
  "id": "job_5Ev2Ridgeview0912",
  "object": "job",
  "type": "hhw_event",
  "client": "cli_2Rv8Ridgeview",
  "site": "site_4Kp1Fairgrounds",
  "status": "billed",
  "scheduled_for": "2026-09-12",
  "window": {
    "start": "08:00",
    "end": "14:00"
  },
  "crew": [
    {
      "name": "Dana Ortiz",
      "role": "Site lead",
      "accepted": true
    },
    {
      "name": "Sam Patel",
      "role": "Chemist",
      "accepted": true
    },
    {
      "name": "Lee Moreno",
      "role": "Technician",
      "accepted": true
    }
  ],
  "container_count": 18,
  "invoice": "inv_1040Ridgeview",
  "metadata": {},
  "created_at": "2026-07-01T14:00:00Z",
  "updated_at": "2026-09-15T14:00:00Z"
}

List jobs

GET/v1/jobs

Returns jobs, soonest first.

Scope jobs:read · Returns 200

Query parameters

  • typeenum

    Only jobs of this type.

    hhw_eventci_pickuplab_pack

  • statusenum

    Only jobs in this status.

    draftscheduledin_progressclosedbilledcancelled

  • clientstring

    Only this client's jobs.

  • scheduled_fromdate

    On or after this date, YYYY-MM-DD.

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/jobs
curl "https://mercovi.com/api/sandbox/v1/jobs?type=hhw_event" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "job_5Ev2Ridgeview0912",
      "object": "job",
      "type": "hhw_event",
      "client": "cli_2Rv8Ridgeview",
      "site": "site_4Kp1Fairgrounds",
      "status": "billed",
      "scheduled_for": "2026-09-12",
      "window": {
        "start": "08:00",
        "end": "14:00"
      },
      "crew": [
        {
          "name": "Dana Ortiz",
          "role": "Site lead",
          "accepted": true
        },
        {
          "name": "Sam Patel",
          "role": "Chemist",
          "accepted": true
        },
        {
          "name": "Lee Moreno",
          "role": "Technician",
          "accepted": true
        }
      ],
      "container_count": 18,
      "invoice": "inv_1040Ridgeview",
      "metadata": {},
      "created_at": "2026-07-01T14:00:00Z",
      "updated_at": "2026-09-15T14:00:00Z"
    },
    {
      "id": "job_8Wb2Westbrook1017",
      "object": "job",
      "type": "hhw_event",
      "client": "cli_9Wb1Westbrook",
      "site": "site_4Kp1Fairgrounds",
      "status": "scheduled",
      "scheduled_for": "2026-10-17",
      "window": {
        "start": "09:00",
        "end": "13:00"
      },
      "crew": [
        {
          "name": "Dana Ortiz",
          "role": "Site lead",
          "accepted": true
        }
      ],
      "container_count": 0,
      "invoice": null,
      "metadata": {},
      "created_at": "2026-08-12T14:00:00Z",
      "updated_at": "2026-10-01T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create a job

POST/v1/jobs

Creates a job in draft. Add a date and window and it moves to scheduled.

Scope jobs:write · Returns 201

Body

  • typeenumrequired

    What kind of work it is.

    hhw_eventci_pickuplab_pack

  • clientstringrequired

    The client's ID.

  • sitestringrequired

    The site's ID.

  • scheduled_fordate

    The date, YYYY-MM-DD.

  • windowobject

    start and end, HH:MM.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

POST /v1/jobs
curl -X POST "https://mercovi.com/api/sandbox/v1/jobs" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "ci_pickup",
    "client": "cli_7Hd3Halden",
    "site": "site_8Tn5HaldenDock",
    "scheduled_for": "2026-11-04",
    "window": {
      "start": "08:00",
      "end": "12:00"
    }
  }'
Response201
{
  "id": "job_7Gx2Sample0001",
  "object": "job",
  "status": "scheduled",
  "scheduled_for": "2026-11-04",
  "window": {
    "start": "08:00",
    "end": "12:00"
  },
  "crew": [],
  "container_count": 0,
  "invoice": null,
  "metadata": {},
  "created_at": "2026-10-09T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z",
  "type": "ci_pickup",
  "client": "cli_7Hd3Halden",
  "site": "site_8Tn5HaldenDock"
}

Retrieve a job

GET/v1/jobs/{id}

Returns one job with its crew.

Scope jobs:read · Returns 200

Path parameters

  • idstringrequired

    The job's ID, beginning job_.

GET /v1/jobs/job_5Ev2Ridgeview0912
curl "https://mercovi.com/api/sandbox/v1/jobs/job_5Ev2Ridgeview0912" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "job_5Ev2Ridgeview0912",
  "object": "job",
  "type": "hhw_event",
  "client": "cli_2Rv8Ridgeview",
  "site": "site_4Kp1Fairgrounds",
  "status": "billed",
  "scheduled_for": "2026-09-12",
  "window": {
    "start": "08:00",
    "end": "14:00"
  },
  "crew": [
    {
      "name": "Dana Ortiz",
      "role": "Site lead",
      "accepted": true
    },
    {
      "name": "Sam Patel",
      "role": "Chemist",
      "accepted": true
    },
    {
      "name": "Lee Moreno",
      "role": "Technician",
      "accepted": true
    }
  ],
  "container_count": 18,
  "invoice": "inv_1040Ridgeview",
  "metadata": {},
  "created_at": "2026-07-01T14:00:00Z",
  "updated_at": "2026-09-15T14:00:00Z"
}

Update a job

PATCH/v1/jobs/{id}

Reschedules or annotates a job that isn't closed.

Scope jobs:write · Returns 200

Path parameters

  • idstringrequired

    The job's ID, beginning job_.

Body

  • scheduled_fordate

    The new date.

  • windowobject

    start and end, HH:MM.

  • metadataobject

    Up to 20 key-value pairs of your own, for example the record's ID in your system. Mercovi stores them and never reads them.

PATCH /v1/jobs/job_6Cp4HaldenNov
curl -X PATCH "https://mercovi.com/api/sandbox/v1/jobs/job_6Cp4HaldenNov" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Content-Type: application/json" \
  -d '{
    "window": {
      "start": "09:00",
      "end": "13:00"
    }
  }'
Response200
{
  "id": "job_6Cp4HaldenNov",
  "object": "job",
  "type": "ci_pickup",
  "client": "cli_7Hd3Halden",
  "site": "site_8Tn5HaldenDock",
  "status": "scheduled",
  "scheduled_for": "2026-11-04",
  "window": {
    "start": "09:00",
    "end": "13:00"
  },
  "crew": [
    {
      "name": "Jordan Kim",
      "role": "Driver",
      "accepted": true
    }
  ],
  "container_count": 4,
  "invoice": null,
  "metadata": {
    "po": "HM-4471"
  },
  "created_at": "2026-09-30T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z"
}

Cancel a job

POST/v1/jobs/{id}/cancel

Cancels a job that hasn't started. The crew is told; nothing is billed.

Scope jobs:write · Returns 200

Path parameters

  • idstringrequired

    The job's ID, beginning job_.

Body

  • reasonstring

    Shown to the crew and on the job's history.

POST /v1/jobs/job_6Cp4HaldenNov/cancel
curl -X POST "https://mercovi.com/api/sandbox/v1/jobs/job_6Cp4HaldenNov/cancel" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Client rescheduled to December."
  }'
Response200
{
  "id": "job_6Cp4HaldenNov",
  "object": "job",
  "type": "ci_pickup",
  "client": "cli_7Hd3Halden",
  "site": "site_8Tn5HaldenDock",
  "status": "cancelled",
  "scheduled_for": "2026-11-04",
  "window": {
    "start": "08:00",
    "end": "12:00"
  },
  "crew": [
    {
      "name": "Jordan Kim",
      "role": "Driver",
      "accepted": true
    }
  ],
  "container_count": 4,
  "invoice": null,
  "metadata": {
    "po": "HM-4471"
  },
  "created_at": "2026-09-30T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z"
}

Containers

A drum, tote, box or cart on a job, from the moment it's opened until it ships. At a facility it carries a storage status: where it stands against the limit that applies to that container.

The container object · 10 attributes

The container object

  • idstringrequired

    Unique identifier for the container, beginning cont_.

  • objectstring

    Always container.

  • jobstring

    The job's ID.

  • profilestring

    The waste profile in it.

  • kindstring

    Type and size, for example 55-gal steel drum.

  • statusenum

    Where the container is.

    openfullreceivedconsolidatedshipped

  • weight_lbinteger

    Net weight in pounds, once weighed.

  • storage_statusenum

    At a facility: where the container stands against the limit that applies to it. Null before receipt.

    within_limitapproaching_limitat_limit

  • received_attimestamp

    When it was checked in at a facility.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

The container object
{
  "id": "cont_1Dr0Flammables",
  "object": "container",
  "job": "job_3Lp9VantageOct",
  "profile": null,
  "kind": "55-gal fiber drum",
  "status": "full",
  "weight_lb": 210,
  "storage_status": null,
  "received_at": null,
  "created_at": "2026-10-08T14:00:00Z"
}

List containers

GET/v1/containers

Returns containers, newest first.

Scope containers:read · Returns 200

Query parameters

  • jobstring

    Only this job's containers.

  • statusenum

    Only containers in this status.

    openfullreceivedconsolidatedshipped

  • storage_statusenum

    Only containers with this storage status.

    within_limitapproaching_limitat_limit

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/containers
curl "https://mercovi.com/api/sandbox/v1/containers?storage_status=approaching_limit" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "cont_7Pt2Paint",
      "object": "container",
      "job": "job_5Ev2Ridgeview0912",
      "profile": "prof_9Wq2Paint",
      "kind": "Cubic-yard box",
      "status": "received",
      "weight_lb": 1140,
      "storage_status": "approaching_limit",
      "received_at": "2026-09-12T14:00:00Z",
      "created_at": "2026-09-12T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Retrieve a container

GET/v1/containers/{id}

Returns one container.

Scope containers:read · Returns 200

Path parameters

  • idstringrequired

    The container's ID, beginning cont_.

GET /v1/containers/cont_1Dr0Flammables
curl "https://mercovi.com/api/sandbox/v1/containers/cont_1Dr0Flammables" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "cont_1Dr0Flammables",
  "object": "container",
  "job": "job_3Lp9VantageOct",
  "profile": null,
  "kind": "55-gal fiber drum",
  "status": "full",
  "weight_lb": 210,
  "storage_status": null,
  "received_at": null,
  "created_at": "2026-10-08T14:00:00Z"
}

Update a container

PATCH/v1/containers/{id}

Records a weight or moves the container to its next status.

Scope containers:write · Returns 200

Path parameters

  • idstringrequired

    The container's ID, beginning cont_.

Body

  • statusenum

    The next status. Moves forward only.

    openfullreceivedconsolidatedshipped

  • weight_lbinteger

    Net weight in pounds.

PATCH /v1/containers/cont_3Dr0Oxidizers
curl -X PATCH "https://mercovi.com/api/sandbox/v1/containers/cont_3Dr0Oxidizers" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "full",
    "weight_lb": 85
  }'
Response200
{
  "id": "cont_3Dr0Oxidizers",
  "object": "container",
  "job": "job_3Lp9VantageOct",
  "profile": "prof_6Ox4Oxidizer",
  "kind": "30-gal metal drum",
  "status": "full",
  "weight_lb": 85,
  "storage_status": null,
  "received_at": null,
  "created_at": "2026-10-08T14:00:00Z"
}

Manifests

A hazardous waste manifest built from a job's containers and profiles. Each manifest carries its EPA e-Manifest status, submission method and tracking number on the record. In the sandbox, tracking numbers are samples.

The manifest object · 11 attributes

The manifest object

  • idstringrequired

    Unique identifier for the manifest, beginning man_.

  • objectstring

    Always manifest.

  • jobstring

    The job's ID.

  • manifest_tracking_numberstring

    The manifest tracking number: nine digits and three letters.

  • statusenum

    Where the manifest is.

    draftready_for_signaturesubmittedsignedcorrected

  • submission_typeenum

    The EPA e-Manifest submission method.

    FullElectronicHybridDataImage5CopyImage

  • generatorobject

    name and epa_id.

  • designated_facilityobject

    name and epa_id.

  • linesarray

    One per waste: profile, dot_description, waste_codes, containers, quantity, unit.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

  • updated_attimestamp

    When the record last changed, ISO 8601 in UTC.

The manifest object
{
  "id": "man_0Mt1Ridgeview",
  "object": "manifest",
  "job": "job_5Ev2Ridgeview0912",
  "manifest_tracking_number": "000123456ELC",
  "status": "signed",
  "submission_type": "Hybrid",
  "generator": {
    "name": "Ridgeview County HHW program",
    "epa_id": "NJD987654321"
  },
  "designated_facility": {
    "name": "Ashfield Chemical Recovery",
    "epa_id": "OHD000111222"
  },
  "lines": [
    {
      "profile": "prof_9Wq2Paint",
      "dot_description": "UN1263, Waste Paint, 3, PG II",
      "waste_codes": [
        "D001"
      ],
      "containers": 1,
      "quantity": 1140,
      "unit": "P"
    },
    {
      "profile": "prof_1Ac7Batteries",
      "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
      "waste_codes": [
        "D002",
        "D008"
      ],
      "containers": 1,
      "quantity": 620,
      "unit": "P"
    }
  ],
  "created_at": "2026-09-12T14:00:00Z",
  "updated_at": "2026-09-13T14:00:00Z"
}

List manifests

GET/v1/manifests

Returns manifests, newest first.

Scope manifests:read · Returns 200

Query parameters

  • jobstring

    Only this job's manifests.

  • statusenum

    Only manifests in this status.

    draftready_for_signaturesubmittedsignedcorrected

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/manifests
curl "https://mercovi.com/api/sandbox/v1/manifests?status=signed" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "man_0Mt1Ridgeview",
      "object": "manifest",
      "job": "job_5Ev2Ridgeview0912",
      "manifest_tracking_number": "000123456ELC",
      "status": "signed",
      "submission_type": "Hybrid",
      "generator": {
        "name": "Ridgeview County HHW program",
        "epa_id": "NJD987654321"
      },
      "designated_facility": {
        "name": "Ashfield Chemical Recovery",
        "epa_id": "OHD000111222"
      },
      "lines": [
        {
          "profile": "prof_9Wq2Paint",
          "dot_description": "UN1263, Waste Paint, 3, PG II",
          "waste_codes": [
            "D001"
          ],
          "containers": 1,
          "quantity": 1140,
          "unit": "P"
        },
        {
          "profile": "prof_1Ac7Batteries",
          "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
          "waste_codes": [
            "D002",
            "D008"
          ],
          "containers": 1,
          "quantity": 620,
          "unit": "P"
        }
      ],
      "created_at": "2026-09-12T14:00:00Z",
      "updated_at": "2026-09-13T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create a manifest

POST/v1/manifests

Builds a draft manifest from a job: one line per profile, waste codes from the contents.

Scope manifests:write · Returns 201

Body

  • jobstringrequired

    The job's ID.

  • designated_facilitystringrequired

    The receiving facility's EPA ID.

  • submission_typeenum

    Defaults to Hybrid.

    FullElectronicHybridDataImage5CopyImage

POST /v1/manifests
curl -X POST "https://mercovi.com/api/sandbox/v1/manifests" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "job": "job_6Cp4HaldenNov",
    "designated_facility": "OHD000111222",
    "submission_type": "Hybrid"
  }'
Response201
{
  "id": "man_7Gx2Sample0001",
  "object": "manifest",
  "job": "job_6Cp4HaldenNov",
  "manifest_tracking_number": null,
  "status": "draft",
  "submission_type": "Hybrid",
  "generator": {
    "name": "Halden Manufacturing",
    "epa_id": "OHD987654321"
  },
  "designated_facility": {
    "name": "Ashfield Chemical Recovery",
    "epa_id": "OHD000111222"
  },
  "lines": [],
  "created_at": "2026-10-09T14:00:00Z",
  "updated_at": "2026-10-09T14:00:00Z"
}

Retrieve a manifest

GET/v1/manifests/{id}

Returns one manifest with its lines.

Scope manifests:read · Returns 200

Path parameters

  • idstringrequired

    The manifest's ID, beginning man_.

GET /v1/manifests/man_0Mt1Ridgeview
curl "https://mercovi.com/api/sandbox/v1/manifests/man_0Mt1Ridgeview" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "man_0Mt1Ridgeview",
  "object": "manifest",
  "job": "job_5Ev2Ridgeview0912",
  "manifest_tracking_number": "000123456ELC",
  "status": "signed",
  "submission_type": "Hybrid",
  "generator": {
    "name": "Ridgeview County HHW program",
    "epa_id": "NJD987654321"
  },
  "designated_facility": {
    "name": "Ashfield Chemical Recovery",
    "epa_id": "OHD000111222"
  },
  "lines": [
    {
      "profile": "prof_9Wq2Paint",
      "dot_description": "UN1263, Waste Paint, 3, PG II",
      "waste_codes": [
        "D001"
      ],
      "containers": 1,
      "quantity": 1140,
      "unit": "P"
    },
    {
      "profile": "prof_1Ac7Batteries",
      "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
      "waste_codes": [
        "D002",
        "D008"
      ],
      "containers": 1,
      "quantity": 620,
      "unit": "P"
    }
  ],
  "created_at": "2026-09-12T14:00:00Z",
  "updated_at": "2026-09-13T14:00:00Z"
}

Invoices

The bill a closed job writes. Amounts are integers in cents.

The invoice object · 11 attributes

The invoice object

  • idstringrequired

    Unique identifier for the invoice, beginning inv_.

  • objectstring

    Always invoice.

  • numberstring

    The invoice number on the document.

  • clientstring

    The client's ID.

  • jobsarray

    The job IDs it bills.

  • statusenum

    Where the invoice is.

    draftopenpaidvoid

  • currencystring

    Three-letter ISO code, lowercase.

  • amount_dueinteger

    Total in cents.

  • amount_paidinteger

    Paid so far, in cents.

  • due_datedate

    When payment is due.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

The invoice object
{
  "id": "inv_1040Ridgeview",
  "object": "invoice",
  "number": "1040",
  "client": "cli_2Rv8Ridgeview",
  "jobs": [
    "job_5Ev2Ridgeview0912"
  ],
  "status": "open",
  "currency": "usd",
  "amount_due": 1846500,
  "amount_paid": 0,
  "due_date": "2026-10-15",
  "created_at": "2026-09-15T14:00:00Z"
}

List invoices

GET/v1/invoices

Returns invoices, newest first.

Scope invoices:read · Returns 200

Query parameters

  • clientstring

    Only this client's invoices.

  • statusenum

    Only invoices in this status.

    draftopenpaidvoid

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/invoices
curl "https://mercovi.com/api/sandbox/v1/invoices?status=open" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "inv_1040Ridgeview",
      "object": "invoice",
      "number": "1040",
      "client": "cli_2Rv8Ridgeview",
      "jobs": [
        "job_5Ev2Ridgeview0912"
      ],
      "status": "open",
      "currency": "usd",
      "amount_due": 1846500,
      "amount_paid": 0,
      "due_date": "2026-10-15",
      "created_at": "2026-09-15T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Retrieve an invoice

GET/v1/invoices/{id}

Returns one invoice.

Scope invoices:read · Returns 200

Path parameters

  • idstringrequired

    The invoice's ID, beginning inv_.

GET /v1/invoices/inv_1040Ridgeview
curl "https://mercovi.com/api/sandbox/v1/invoices/inv_1040Ridgeview" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "inv_1040Ridgeview",
  "object": "invoice",
  "number": "1040",
  "client": "cli_2Rv8Ridgeview",
  "jobs": [
    "job_5Ev2Ridgeview0912"
  ],
  "status": "open",
  "currency": "usd",
  "amount_due": 1846500,
  "amount_paid": 0,
  "due_date": "2026-10-15",
  "created_at": "2026-09-15T14:00:00Z"
}

Webhook endpoints

A URL of yours that Mercovi calls when something happens. Each delivery is signed so you can check it came from Mercovi.

The webhook_endpoint object · 7 attributes

The webhook_endpoint object

  • idstringrequired

    Unique identifier for the endpoint, beginning we_.

  • objectstring

    Always webhook_endpoint.

  • urlstringrequired

    An HTTPS URL.

  • eventsarray

    Event types to send, or ["*"] for all.

  • secretstring

    The signing secret, beginning whsec_. Returned once, on create.

  • enabledboolean

    Whether deliveries are on.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

The webhook_endpoint object
{
  "id": "we_4Hk9Erp",
  "object": "webhook_endpoint",
  "url": "https://erp.example.com/hooks/mercovi",
  "events": [
    "manifest.signed",
    "invoice.sent",
    "invoice.paid"
  ],
  "secret": null,
  "enabled": true,
  "created_at": "2026-09-01T14:00:00Z"
}

List webhook endpoints

GET/v1/webhook_endpoints

Returns your endpoints.

Scope webhooks:read · Returns 200

Query parameters

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/webhook_endpoints
curl "https://mercovi.com/api/sandbox/v1/webhook_endpoints" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "we_4Hk9Erp",
      "object": "webhook_endpoint",
      "url": "https://erp.example.com/hooks/mercovi",
      "events": [
        "manifest.signed",
        "invoice.sent",
        "invoice.paid"
      ],
      "secret": null,
      "enabled": true,
      "created_at": "2026-09-01T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create a webhook endpoint

POST/v1/webhook_endpoints

Registers a URL. The response carries the signing secret — store it; it isn't shown again.

Scope webhooks:write · Returns 201

Body

  • urlstringrequired

    An HTTPS URL.

  • eventsarrayrequired

    Event types, or ["*"].

POST /v1/webhook_endpoints
curl -X POST "https://mercovi.com/api/sandbox/v1/webhook_endpoints" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://erp.example.com/hooks/mercovi",
    "events": [
      "manifest.signed",
      "invoice.sent"
    ]
  }'
Response201
{
  "id": "we_7Gx2Sample0001",
  "object": "webhook_endpoint",
  "enabled": true,
  "secret": "whsec_7Gx2Sample00017Gx2Sample0001",
  "created_at": "2026-10-09T14:00:00Z",
  "url": "https://erp.example.com/hooks/mercovi",
  "events": [
    "manifest.signed",
    "invoice.sent"
  ]
}

Delete a webhook endpoint

DEL/v1/webhook_endpoints/{id}

Stops deliveries and removes the endpoint.

Scope webhooks:write · Returns 200

Path parameters

  • idstringrequired

    The endpoint's ID, beginning we_.

DELETE /v1/webhook_endpoints/we_4Hk9Erp
curl -X DELETE "https://mercovi.com/api/sandbox/v1/webhook_endpoints/we_4Hk9Erp" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "we_4Hk9Erp",
  "object": "webhook_endpoint",
  "deleted": true
}

Events

A record of something that happened. The same object is what a webhook delivers, and you can list recent events to catch up after downtime.

The event object · 5 attributes

The event object

  • idstringrequired

    Unique identifier for the event, beginning evt_.

  • objectstring

    Always event.

  • typeenum

    What happened.

    job.createdjob.scheduledjob.closedstaff.acceptedcontainer.receivedcontainer.storage_status_changedmanifest.ready_for_signaturemanifest.signedprofile.approvedprofile.expiringinvoice.sentinvoice.paid

  • dataobject

    object: the record as it was right after the change.

  • created_attimestamp

    When the record was created, ISO 8601 in UTC.

The event object
{
  "id": "evt_8Qs1ManSigned",
  "object": "event",
  "type": "manifest.signed",
  "data": {
    "object": {
      "id": "man_0Mt1Ridgeview",
      "object": "manifest",
      "job": "job_5Ev2Ridgeview0912",
      "manifest_tracking_number": "000123456ELC",
      "status": "signed",
      "submission_type": "Hybrid",
      "generator": {
        "name": "Ridgeview County HHW program",
        "epa_id": "NJD987654321"
      },
      "designated_facility": {
        "name": "Ashfield Chemical Recovery",
        "epa_id": "OHD000111222"
      },
      "lines": [
        {
          "profile": "prof_9Wq2Paint",
          "dot_description": "UN1263, Waste Paint, 3, PG II",
          "waste_codes": [
            "D001"
          ],
          "containers": 1,
          "quantity": 1140,
          "unit": "P"
        },
        {
          "profile": "prof_1Ac7Batteries",
          "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
          "waste_codes": [
            "D002",
            "D008"
          ],
          "containers": 1,
          "quantity": 620,
          "unit": "P"
        }
      ],
      "created_at": "2026-09-12T14:00:00Z",
      "updated_at": "2026-09-13T14:00:00Z"
    }
  },
  "created_at": "2026-09-13T14:00:00Z"
}

List events

GET/v1/events

Returns recent events, newest first.

Scope events:read · Returns 200

Query parameters

  • typestring

    Only this event type.

  • limitinteger

    How many records to return, 1 to 100. Defaults to 25.

  • starting_afterstring

    A record ID. Returns the page after it — pass the next_cursor of the previous page.

GET /v1/events
curl "https://mercovi.com/api/sandbox/v1/events?type=manifest.signed" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "object": "list",
  "data": [
    {
      "id": "evt_8Qs1ManSigned",
      "object": "event",
      "type": "manifest.signed",
      "data": {
        "object": {
          "id": "man_0Mt1Ridgeview",
          "object": "manifest",
          "job": "job_5Ev2Ridgeview0912",
          "manifest_tracking_number": "000123456ELC",
          "status": "signed",
          "submission_type": "Hybrid",
          "generator": {
            "name": "Ridgeview County HHW program",
            "epa_id": "NJD987654321"
          },
          "designated_facility": {
            "name": "Ashfield Chemical Recovery",
            "epa_id": "OHD000111222"
          },
          "lines": [
            {
              "profile": "prof_9Wq2Paint",
              "dot_description": "UN1263, Waste Paint, 3, PG II",
              "waste_codes": [
                "D001"
              ],
              "containers": 1,
              "quantity": 1140,
              "unit": "P"
            },
            {
              "profile": "prof_1Ac7Batteries",
              "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
              "waste_codes": [
                "D002",
                "D008"
              ],
              "containers": 1,
              "quantity": 620,
              "unit": "P"
            }
          ],
          "created_at": "2026-09-12T14:00:00Z",
          "updated_at": "2026-09-13T14:00:00Z"
        }
      },
      "created_at": "2026-09-13T14:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Retrieve an event

GET/v1/events/{id}

Returns one event.

Scope events:read · Returns 200

Path parameters

  • idstringrequired

    The event's ID, beginning evt_.

GET /v1/events/evt_8Qs1ManSigned
curl "https://mercovi.com/api/sandbox/v1/events/evt_8Qs1ManSigned" \
  -H "Authorization: Bearer mk_test_sbx_Ridgeview7Hq2Lk4Wd9"
Response200
{
  "id": "evt_8Qs1ManSigned",
  "object": "event",
  "type": "manifest.signed",
  "data": {
    "object": {
      "id": "man_0Mt1Ridgeview",
      "object": "manifest",
      "job": "job_5Ev2Ridgeview0912",
      "manifest_tracking_number": "000123456ELC",
      "status": "signed",
      "submission_type": "Hybrid",
      "generator": {
        "name": "Ridgeview County HHW program",
        "epa_id": "NJD987654321"
      },
      "designated_facility": {
        "name": "Ashfield Chemical Recovery",
        "epa_id": "OHD000111222"
      },
      "lines": [
        {
          "profile": "prof_9Wq2Paint",
          "dot_description": "UN1263, Waste Paint, 3, PG II",
          "waste_codes": [
            "D001"
          ],
          "containers": 1,
          "quantity": 1140,
          "unit": "P"
        },
        {
          "profile": "prof_1Ac7Batteries",
          "dot_description": "UN2794, Batteries, wet, filled with acid, 8, PG III",
          "waste_codes": [
            "D002",
            "D008"
          ],
          "containers": 1,
          "quantity": 620,
          "unit": "P"
        }
      ],
      "created_at": "2026-09-12T14:00:00Z",
      "updated_at": "2026-09-13T14:00:00Z"
    }
  },
  "created_at": "2026-09-13T14:00:00Z"
}
Try it · sandbox