---
title: Errors
description: Every Doing Work API error code, its status, what it means, and what an agent should do.
url: https://doingwork.work/docs/errors/
---

# Errors

Errors are JSON: `{"code": "...", "message": "...", "status": 400}`. The message is for you; the code is stable.

| Code | Status | Meaning and what to do |
|---|---|---|
| `invalid_input` | 400 | A field is missing, the wrong type, or unknown. The message names the field. Fix the input; don't retry as is. |
| `disclosure_ack_required` | 400 | The customer must acknowledge the Idaho disclosure on the hosted signing page. |
| `terms_not_accepted` | 400 |  |
| `terms_not_published` | 503 |  |
| `consent_required` | 400 | A person must tick a consent box on a hosted page (for example e-sign). Send them the link. |
| `unsupported_version` | 400 | The Doing-Work-Version header isn't one the API knows. Use 2026-11-01. |
| `bad_signature` | 400 | A signature didn't verify. |
| `invalid_link` | 400 | The link was used, expired, or replaced by a newer one. Get a new link. |
| `invalid_request` | 400 | An OAuth request is malformed. |
| `invalid_grant` | 400 | The code, verifier or refresh token is wrong, used or expired. Start sign-in again. |
| `unsupported_grant_type` | 400 | Use authorization_code or refresh_token. |
| `invalid_target` | 400 | The resource parameter doesn't match this server. |
| `invalid_scope` | 400 | The requested scope isn't offered. |
| `invalid_client_metadata` | 400 | The client registration is invalid, for example a redirect URI that isn't https or loopback. |
| `invalid_claim_token` | 400 |  |
| `issuer_not_enabled` | 400 |  |
| `authorization_pending` | 400 |  |
| `slow_down` | 400 |  |
| `expired_token` | 400 |  |
| `access_denied` | 400 |  |
| `claimed_or_in_flight` | 409 |  |
| `claim_expired` | 410 |  |
| `invalid_token` | 401 | The token is missing, expired, or for a different resource. Refresh it or sign in again. |
| `session_required` | 403 | A person has to be signed in on a browser for this step. |
| `owner_cannot_leave` | 409 | The owner can't leave a shop. Ownership moves only by transfer. |
| `owner_role_fixed` | 409 | The owner's role can't be changed. |
| `unauthenticated` | 401 | No token was sent. Sign in first. |
| `forbidden` | 403 | This worker's role can't do this, or it needs a person's own sign-in rather than an agent token. Tell the person who can. |
| `human_only` | 403 | This step happens on a hosted page the person opens themselves. Send them the link; don't try another route. |
| `feature_unavailable` | 403 | This isn't available for this shop or place yet. |
| `private_beta` | 403 | This is invite only for now. Tell the person to email contact@doingwork.work. |
| `not_found` | 404 | No such record, or this worker can't see it. |
| `conflict` | 409 | The record is in the wrong state for this, for example sending a quote that's already signed. Read it again and pick the next step. |
| `checklist_open` | 409 |  |
| `payment_pending` | 409 | A bank (ACH) payment for this invoice is still clearing, which takes a few business days. Don't ask the customer to pay again. Wait for invoice.paid or invoice.payment_failed. |
| `payments_not_set_up` | 409 | The shop hasn't connected Stripe. The owner does that on a hosted page. |
| `payments_paused` | 409 |  |
| `idempotency_in_progress` | 409 | A request with this Idempotency-Key is still running. Wait and retry with the same key. |
| `idempotency_key_reused` | 422 | This Idempotency-Key was used with a different body. Use a new key for a new request. |
| `unprintable_text` | 422 |  |
| `document_too_large` | 422 |  |
| `rate_limited` | 429 | Too many requests. Wait for the Retry-After time, then retry. |
| `payload_too_large` | 413 |  |
| `internal` | 500 | Something broke on our side. Retry later with the same Idempotency-Key. |
| `not_supported` | 501 | Not supported yet. |
