Skip to main content

Errors and limits

Error body​

Each non-2xx response has the same body:

{
"error": "Bad Request",
"code": "validation_failed",
"message": "Request body failed validation",
"details": {
"formErrors": [],
"fieldErrors": { "user_email": ["Invalid email"] }
}
}
FieldUse
codeStable. Use this field in your code.
messageFor people. It can change. Do not parse it.
errorThe HTTP status label. For information only.
detailsOnly on a validation_failed where the body does not match the request schema. fieldErrors lists the errors for each field, and formErrors the errors on the body as a whole.

Codes​

HTTPcodeCauseRetry
400validation_failedThe body or a query parameter is not valid. message says what is wrong, and details lists the failing fields when the body does not match the request schema.No
400invalid_jsonThe body is not valid JSON.No
400body_requiredNo JSON body, or Content-Type is not application/json.No
400document_path_invalidA storage_path is not in the upload folder for this entity and partner_claim_id, or a message names a file already attached to the claim.No
400document_not_uploadedA file in document_refs is not in storage. The upload did not complete.No
401auth_*See Auth errors.No
403auth_partner_not_approvedGarfield has not approved your partner account yet. Until it does, only test keys work.No
403entity_not_ownedThe entity is not connected to you and is not your own business.No
403partner_disconnectedThe entity disconnected from you. You cannot read or write its claims.No
403entity_not_verifiedGarfield has not verified the business yet, or has rejected it. Uploads, claims and messages fail, and reads still work. Send the failed requests again once GET /entities shows verified: true.No
404entity_not_foundNo entity has this id, or you sent a real entity_id with a test key.No
404claim_not_foundYou have not created a claim with that claim_id using a key of this mode, or you can no longer reach its entity.No
409claim_conflictAn internal id collision. This is rare.Yes
409idempotency_key_reusedYou sent a partner_user_id, partner_claim_id or partner_message_id again with a different body. Garfield added nothing.No
409claim_archivedThe business archived the claim, so you cannot add a message to it.No
409partner_user_id_mismatchThe partner_user_id is not the one the entity connected with. Check the entity_id.No
422onboarding_unavailableGarfield cannot onboard the business automatically. Send a connect link.No
422company_not_foundCompanies House has no company with this company_number.No
422company_not_activeThe company is dissolved, in liquidation or otherwise not active at Companies House.No
422company_type_not_supportedThe company is a type of organisation Garfield does not onboard.No
422business_type_mismatchbusiness_type differs from the company's type at Companies House.No
429rate_limitedToo many requests. See Rate limits.Yes
500internal_errorServer error.Yes
503service_unavailableA dependency is temporarily unavailable.Yes
503document_verification_failedThe API could not check an uploaded document in storage. The fault is temporary.Yes

Retries​

  • Retry only the codes marked Yes. Wait for Retry-After if the response has it. Use exponential backoff with jitter.
  • For all other codes, correct the request before you send it again.
  • When you retry a create, send the same partner_user_id, partner_claim_id or partner_message_id, so the API does not create a second record.
  • Do not resend a request that returned idempotency_key_reused. It fails again. To correct or add to a claim, send a message.

Rate limits​

Each partner can send 120 requests per minute. All endpoints and all of your keys share this limit. Only requests with a verified key count, so no one else can use up your limit by sending your partner id with a wrong secret. Until Garfield approves your application, the limit is 30 requests per minute and each upload can be at most 5 MB.

Requests that fail authentication count separately: 10 per minute for each IP address. Past that, the API answers every failed request with rate_limited until the minute is up. Fix the key before you send more requests.

In production, every response to a request that passed authentication, with a test or live key, has the X-RateLimit-Limit and X-RateLimit-Reset headers, except a 503 service_unavailable when the API could not check the limit:

HeaderMeaning
X-RateLimit-LimitThe maximum requests in each window.
X-RateLimit-ResetThe window length in seconds, always 60. The API does not report how much of the current window is left.
X-RateLimit-RemainingOnly on a 429, where it is 0. The API does not report a running count.

If you go over the limit, the API returns 429 rate_limited with these headers and Retry-After, the number of seconds to wait. Wait that long, then retry. Keep a margin below X-RateLimit-Limit.

Idempotency​

The API uses your ids as idempotency keys. There is no Idempotency-Key header. Each partner has its own id space, so your ids never collide with another partner's.

EndpointKeyReplay result
POST /onboardingspartner_user_idHTTP 200, the first onboarding_id and entity_id, already_existed: true. No new email.
POST /claimspartner_claim_id, within each entityHTTP 200, the first claim_id, message_id and document_ids, already_existed: true. No new message or documents.
POST /claims/{claim_id}/messagespartner_message_id, optional, within each claimHTTP 200, the first message_id and document_ids, already_existed: true. Without it, every call adds a message.
POST /connect-requestsNoneEach call gives a new link.
  • The same partner_claim_id with a different entity_id creates a separate claim.
  • A replay must send the same body. A different body returns 409 idempotency_key_reused, and Garfield keeps what the first request created and adds nothing. The API ignores the order of fields. Items in an array such as invoices must stay in the same order. The API treats a field sent as null or [] the same as a field left out. A claim or onboarding created before Garfield began storing request bodies replays whatever body you send.
  • A POST /claims replay goes through the same checks as a create. If the entity disconnected from you, the replay returns 403 partner_disconnected, and a different partner_user_id returns 409 partner_user_id_mismatch.
  • A claim created before Garfield began storing message and document ids replays with message_id: null and document_ids: [].
  • A replay of POST /onboardings does not send the magic link again, and the API never returns it. Another partner_user_id for the same user_email returns 422 onboarding_unavailable. If the link has expired, the user can open it and ask for a new one, which Garfield emails to the same address.