Skip to content

This is the staging environment's documentation. It is a draft and may change at any time.

Errors

The API reports errors as Problem Details (RFC 9457) with the media type application/problem+json. Every error from the API has this shape, whatever its status.

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Server-Timing: trace;desc=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
{
"type": "https://fedwright.com/problems/bad-request",
"title": "Bad Request",
"status": 400,
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"errors": {
"Idempotency-Key": ["Idempotency-Key must be 8 to 255 characters"]
}
}
Field Always present Meaning
detail No An explanation specific to this occurrence. Absent for server errors.
errors No Validation errors, by field path. Each value is a list of messages.
instance No A URI reference for this occurrence, when the API provides one.
limit No On a rate-limit error, the quota that is used up. See Rate limits.
resetsAt No On a rate-limit error, when that quota starts again, as an ISO 8601 time in UTC.
status Yes The HTTP status code, repeated.
title Yes A short summary. Don’t match on it; use type.
traceId Yes The trace ID of the failed request. Quote it when you ask for help.
type Yes https://fedwright.com/problems/ followed by a code, such as not-found. The same in every environment.

Decide what to do from status and type. Treat title and detail as text for people. They can change.

An unexpected failure (500) returns only type, title, status, and traceId.

The keys of errors are field paths in the request, such as nested.note, or $ for the body as a whole. A header has its own name as the key, such as Idempotency-Key. A request that doesn’t satisfy the contract’s schema is refused with 400 (bad-request) before anything is read or written. A request whose schema is satisfied but that breaks a further rule, such as an unknown NAICS code, is refused with 422 (validation-failed).

Every response, errors and successes alike, carries a Server-Timing header with a trace metric:

Server-Timing: trace;desc=00-{trace-id}-{span-id}-01

The {trace-id} part is the error’s traceId. If a response has no Problem body, for example one from a proxy in front of the API, read the trace ID from this header when it is there. Include the trace ID, the time of the request, and the path when you ask for support. Don’t include tokens.

Type Status When
ai-budget-exceeded 429 An ask that the organization’s AI spending budget can’t cover. Retry-After gives the wait.
ai-unavailable 503 An ask can’t run now, and nothing was charged. Retry after Retry-After seconds.
bad-request 400 The request is malformed or its schema refuses it. Also any other client mistake, such as an unsupported method or media type.
billing-unavailable 503 Paid plans can’t be used now. Nothing was changed.
client-not-allowed 403 The operation isn’t open to the caller’s kind of client, such as a machine client calling a user-only operation.
conflict 409 The request conflicts with the resource’s current state, such as a slug that another organization has.
email-unverified 403 Creating or joining an organization needs a verified email address.
forbidden 403 Any other refusal. It says nothing more.
idempotency-in-progress 409 The first request with this Idempotency-Key is still running. See Idempotency.
idempotency-key-reused 422 The Idempotency-Key was already used for a different request.
insufficient-role 403 A member’s roles don’t allow the action.
insufficient-scope 403 The token’s client lacks the operation’s scope. WWW-Authenticate names the missing scopes.
internal-error 500 An unexpected failure.
invalid-cursor 400 The cursor was altered, expired, or was issued for a different query. See Pagination.
invitation-email-mismatch 403 An invitation was sent to another address.
last-owner 409 The change would leave an organization with no owner.
mail-unavailable 503 An invitation can’t be sent now. Nothing was created.
mfa-required 403 An owner or administrator sent no proof of a second factor.
no-billing-account 409 The billing portal was requested for an organization that has never paid.
not-found 404 The resource doesn’t exist, or belongs to another organization.
organization-required 403 A session of Fedwright’s web or mobile app that acts for no organization yet.
organization-suspended 403 Fedwright suspended the organization.
payload-too-large 413 The request body is over 64 KiB.
precondition-failed 412 If-Match is stale. See Conditional requests.
premium-quota-exceeded 429 An ask that the organization’s premium units for the day can’t cover. See Rate limits.
rate-limited 429 A rate limit is used up. See Rate limits.
request-timeout 503 The request took longer than 10 seconds (90 for askOpportunities).
search-unavailable 503 Search is temporarily unavailable. Retry after Retry-After seconds.
subscription-exists 409 A checkout was started for an organization that already has a paid subscription.
subscription-inactive 403 The operation needs an active subscription and the organization has none.
unauthorized 401 The access token is missing, expired, or invalid, or the caller was disabled or revoked. WWW-Authenticate: Bearer is set.
validation-failed 422 The request is well formed but breaks a rule beyond its schema. errors names each field.

Another organization’s resource is 404, not 403, so a response never confirms that it exists.

Answer Retry?
429 Yes, after the wait the response gives. It is returned before the operation runs, so a retry can’t repeat an effect. See Rate limits.
409 idempotency-in-progress Yes, with the same Idempotency-Key, after Retry-After.
503 with Retry-After (search-unavailable, ai-unavailable) Yes, after the wait. Nothing was charged.
503 request-timeout, 502, 504, a dropped connection Only if the call is safe to repeat: a GET, a PUT, a DELETE, or a POST with an Idempotency-Key.
401 Once, with a new token. See Authentication.
Any other 4xx No. Change the request.

When a call is safe to repeat and the answer has no Retry-After, wait a short time that grows with each attempt, with random jitter, and stop after a few attempts.