Skip to content

Developers

Errors

Every error code the API sends: what causes it, how to fix it, and whether retrying the same request can help.

On this page

The error body

Every error, on every endpoint, is JSON with an HTTP status of 400 or more and this body. Branch on code: it's stable, while message is written for people and its wording can change. request_id matches the X-Request-Id header; log it and quote it when you contact support. details comes with some codes only.

Error body
{
  "error": {
    "code": "NOT_FOUND",
    "message": "No such record, report or path, or one your company can't see (another carrier's record, a draft not yet sent to you).",
    "request_id": "6f1c2d3e-4b5a-4c6d-8e7f-9a0b1c2d3e4f"
  }
}

New codes may be added. Treat a code you don't know by its HTTP status: 4xx means fix the request, 5xx means retry later.

Retry or fix?

  • Retryable codes can work if you send the same request again later. Wait Retry-After seconds when the answer has it, else back off (1 s, 2 s, 4 s...). Reuse the same Idempotency-Key, so a write that did go through isn't made twice.
  • Not retryable codes give the same answer until something changes: fix the request (and use a new Idempotency-Key for the changed request), or re-read the record and decide.

Every code

  • 400VALIDATION_ERRORNot retryable
    Cause
    A field, parameter or header is missing or invalid: a bad date, an unknown status, a limit out of range, a cursor this API didn't return, a malformed Idempotency-Key, disputed_minutes longer than the wait, or an offer at or above the charge. details.fields names each one.
    Fix
    Fix the fields listed in details.fields and send it again with a new Idempotency-Key.
  • 400BAD_REQUESTNot retryable
    Cause
    The request can't be done as sent: the body isn't valid JSON, nothing is billable (the wait was within free time), a driver-app record has no photo yet, or the file doesn't match its type.
    Fix
    Read message, fix the request, and send it with a new Idempotency-Key.
  • 401UNAUTHORIZEDNot retryable
    Cause
    No API key, a malformed Authorization header, or a key that is wrong, revoked, turned off or expired.
    Fix
    Send Authorization: Bearer dmrl_... with an active key from Settings > API Keys.
    Headers
    WWW-Authenticate
  • 403FORBIDDENNot retryable
    Cause
    The key lacks the scope, its creator is no longer an admin (writes), the company is inactive, or only the other side can do this (only carriers report, send and attach photos; only fuel buyers approve and dispute; only carriers list drivers).
    Fix
    Give the key the scope, create a new key as a current admin, or use the right side's key. message says which.
  • 403PLAN_LIMITNot retryable
    Cause
    The carrier's company isn't on the Pro plan, which the API needs (fuel buyers are free).
    Fix
    Upgrade to Pro under Settings > Plan & Billing, then retry.
  • 404NOT_FOUNDNot retryable
    Cause
    No such record, report or path, or one your company can't see (another carrier's record, a draft not yet sent to you).
    Fix
    Check the id (and that it belongs to the key's company); list records to find it.
  • 405METHOD_NOT_ALLOWEDNot retryable
    Cause
    The path exists but not with this method (for example DELETE /api/v1/records).
    Fix
    Use one of the methods in the Allow header.
    Headers
    Allow
  • 409INVALID_STATUSNot retryable
    Cause
    the record's status doesn't allow this (it's paid, closed, cancelled, already approved, or withdrawn from you); details.status says which. Re-read it; don't retry.
    Fix
    GET the record, and act on its current status (see the record lifecycle).
    Headers
    Retry-After
  • 409OVERLAPPING_WAITNot retryable
    Cause
    the driver already has a wait at these times (or this exact one); details.record_id and details.record_number name it.
    Fix
    Use the existing record, or correct the driver or the times.
    Headers
    Retry-After
  • 409CONFLICTNot retryable
    Cause
    another state of the record rules it out (nothing billable, the buyer's payment terms, a changed amount or offer). Re-read it and decide.
    Fix
    For payment rules (details.reason payment_rules), send again with confirm_nonconforming: true if you mean to; otherwise GET the record and decide.
    Headers
    Retry-After
  • 409RECORD_CHANGEDRetryable
    Cause
    another action changed the record first. Wait Retry-After seconds and send the same request again.
    Fix
    Wait Retry-After seconds and send the same request (the same Idempotency-Key is fine).
    Headers
    Retry-After
  • 409RECORD_LOCKEDRetryable
    Cause
    a payment is processing for this record. Wait Retry-After seconds and try again.
    Fix
    Wait Retry-After seconds and send the same request.
    Headers
    Retry-After
  • 409IDEMPOTENCY_IN_PROGRESSRetryable
    Cause
    a request with this Idempotency-Key is still running. Wait Retry-After seconds and send it again.
    Fix
    Wait Retry-After seconds and send it again with the same key; you'll get the first request's answer.
    Headers
    Retry-After
  • 409DELIVERY_IN_PROGRESSRetryable
    Cause
    the webhook delivery you asked to resend is being sent right now. Wait Retry-After seconds, then check the delivery log before resending.
    Fix
    Wait Retry-After seconds, GET the delivery, and resend only if it still failed.
    Headers
    Retry-After
  • 413PAYLOAD_TOO_LARGENot retryable
    Cause
    The body is too big (a photo over 5 MB, a JSON body over its limit), or a saved report's CSV would have more than 50,000 rows.
    Fix
    Send a smaller photo; narrow the report, pass limit, or use format=json.
  • 415UNSUPPORTED_MEDIA_TYPENot retryable
    Cause
    The Content-Type isn't one this endpoint takes: a JSON endpoint got a body that isn't application/json (or +json), or a photo isn't image/jpeg or image/png.
    Fix
    Send Content-Type: application/json with a JSON body (a write with no body needs none), or image/jpeg or image/png for a photo.
  • 422IDEMPOTENCY_KEY_REUSEDNot retryable
    Cause
    This Idempotency-Key was already used for a different request (the body must be byte-identical); use a new key for a changed request
    Fix
    Send a changed request with a new key. To retry the first request, resend exactly the same bytes with the same key.
  • 429RATE_LIMITEDRetryable
    Cause
    Too many requests this minute: over the key's own limit (60 by default), over the company's 3,000 a minute across all its keys, or over 60 a minute with a missing or invalid key from one IP address.
    Fix
    Wait Retry-After seconds, then retry. Spread requests out (X-RateLimit-Remaining and X-RateLimit-Reset say how many are left until when), or raise the key's limit (up to 1,000) in Settings > API Keys.
    Headers
    Retry-After
  • 500INTERNAL_ERRORRetryable
    Cause
    Something went wrong on Demurly's side.
    Fix
    Retry with backoff (1 s, 2 s, 4 s...) and the same Idempotency-Key; if it keeps happening, email support with the time and the request.
  • 503SERVICE_UNAVAILABLERetryable
    Cause
    The same change is still being written by an earlier request, or Demurly is briefly unavailable.
    Fix
    Wait Retry-After seconds and send the same request with the same Idempotency-Key.
    Headers
    Retry-After

Each endpoint lists the statuses it can answer with on the reference.

details shapes

details is an object whose shape depends on the code. Ignore keys you don't know; new ones may be added.

Payment rules details

CONFLICT from send: the charge doesn't meet the buyer's payment rules. Send again with confirm_nonconforming: true to send it anyway.

  • reasonstring
    payment_rules
  • problemsarray of object
  • problems[].rulestring
    min_free_time
    The buyer pays only after more free time than this charge gives
    max_rate
    The charge's hourly rate is above the buyer's maximum
  • problems[].scopestring
    carrier
    The buyer set the rule for your company
    company
    The buyer's rule for every carrier
  • problems[].messagestring

    The problem in words

  • problems[].required_minutesinteger

    min_free_time: the free time the buyer expects

  • problems[].actual_minutesinteger

    min_free_time: the free time this charge gives

  • problems[].max_rate_centsinteger

    max_rate: the buyer's maximum hourly rate, in cents

  • problems[].actual_rate_centsinteger

    max_rate: this charge's hourly rate, in cents

  • confirm_withstring

    The request field that sends it anyway

    confirm_nonconforming
Payment rules details (example)
{
  "reason": "payment_rules",
  "problems": [
    {
      "rule": "max_rate",
      "scope": "carrier",
      "message": "The buyer's maximum rate is $50.00 an hour; this charge is $100.00.",
      "max_rate_cents": 5000,
      "actual_rate_cents": 10000
    }
  ],
  "confirm_with": "confirm_nonconforming"
}

Overlapping wait details

OVERLAPPING_WAIT: the driver's other wait

  • reasonstring
    overlapping_wait
  • record_idstring (uuid)
  • record_numberstring
  • start_timestring (date-time)
  • end_timestring (date-time)can be null
  • statusstring
Overlapping wait details (example)
{
  "reason": "overlapping_wait",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "record_number": "DMR-00001042",
  "start_time": "2026-01-15T14:30:00.000Z",
  "end_time": "2026-01-15T17:45:00.000Z",
  "status": "sent_to_customer"
}

Invalid status details

INVALID_STATUS: the status the record is in

  • statusstring

    A record status, or withdrawn for a charge withdrawn from you

Invalid status details (example)
{
  "status": "customer_approved"
}

Validation error details

VALIDATION_ERROR: what's wrong with each field

  • fieldsobject

    Field name (dotted for nested fields; "_" for the body as a whole) to its messages

Validation error details (example)
{
  "fields": {
    "start_time": [
      "Required"
    ],
    "end_time": [
      "end_time can't be in the future"
    ]
  }
}
NextKeep your system in syncPolling with updated_since and cursors, tombstones and webhooks together.
API errors