Errors and responses
Gateman does not impose one universal JSON response envelope. The response depends on the capability you call.
Gateman responses fall into four families:
- Gateman-generated JSON responses.
- Provider-native HTTP responses returned through provider relays.
- Public client acquisition artifacts.
- Webhook responses.
For application integrations, use the HTTP status, response headers, and the response contract for the selected capability. Do not assume that every successful response contains ok: true, or that every failure contains ok: false.
Shared HTTP behavior
Request correlation
Gateman generates X-Gateman-Request-ID for machine API requests. It is a 16-character lowercase hexadecimal identifier and is returned on normal responses, including authentication failures. It is useful when investigating a request and is distinct from any request identifier generated by a provider.
The caller cannot choose or override it. Keep it with application logs and support reports. Do not put credentials or secret values in those logs.
Headers and content types
Gateman-generated machine responses normally include:
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
X-Gateman-Request-ID: <request identifier>
These headers are not a promise that every deployment component has an identical header set. Provider relays may replace the content type with the provider's content type, and client acquisition routes return downloadable artifacts.
Retry-After appears only where it applies, such as some concurrency, rate-limit, and provider-throttling responses. It is not guaranteed on every 429 or 503 response.
Gateman-generated JSON
A successful Gateman-generated response has a route-specific shape. For example, GET /health returns:
{
"ok": true,
"service": "gateman",
"version": "1.0.0",
"time": "2026-10-10T00:00:00+00:00"
}
A common Gateman-generated error shape is:
{
"ok": false,
"error": "Invalid JSON"
}
ok: false and an error string are common conventions, not a universal schema. Some routes add capability-specific fields such as provider information, usage limits, retry information, or an AWS error code. There is no universal Gateman error-code namespace. Provider-native errors may use a completely different body structure.
HTTP status codes
These are statuses currently generated by Gateman itself:
| Status | Current use |
|---|---|
200 | Successful Gateman operations and acknowledgements. |
400 | Invalid input or capability-specific request rejection. |
401 | Authentication rejection. |
403 | Monthly operation allowance exhausted or an email sending policy rejection. |
404 | Unknown route or missing resource. |
409 | Credential replay, idempotency conflict, or indeterminate send outcome. |
410 | Retired endpoint. |
429 | Gateman concurrency/rate limiting or a provider rate-limit result translated by a capability. |
500 | Internal capability, webhook, or credential-store failure. |
502 | Provider or capability-boundary failure. |
503 | Unavailable authentication, infrastructure, provider, admission, or accounting dependency. |
Provider relays may return other statuses directly from the provider, including 201, 204, 422, and provider-specific 4xx or 5xx statuses. A wrong method/path combination generally reaches Gateman's unmatched-route response and returns 404; do not assume a universal 405 response.
The status is authoritative. A received HTTP error response is different from a transport failure where no HTTP response was received.
Authentication and authorization errors
Deterministic authentication rejection returns 401. Examples include:
- Missing or invalid application authentication.
- Invalid signature.
- Expired, future, or malformed timestamp.
- Unknown or inactive application.
- Missing required scope for routes other than the email sending policy errors described below.
- Source-IP rejection.
- Deterministically invalid stored authentication material.
Genuine inability to complete authentication because the application registry or authentication KMS/database dependency is unavailable returns 503:
{
"ok": false,
"error": "Authentication unavailable"
}
Incorrect credentials are not the same as authentication infrastructure failure. Do not automatically retry invalid signatures, missing scopes, or other deterministic authentication rejections.
See Authentication and signing and Contract v1 for the authentication contract.
Email sending policy errors
Email routes use stable 403 errors for the application-wide sending policy:
scope_not_allowedโ theemail.sendcapability is not granted.email_sending_suspendedโ Sending Protection has deliberately suspended email sending.email_sending_limit_exceededโ the current request would exceed the configured automatic sending limit; the request is rejected and sending is suspended.
Applications should stop automatic retries for these errors. Check Sending Protection and use an authorised recovery path before investigating provider credentials, networking, or provider outages.
Request validation
Malformed or unsupported requests commonly return 400, including invalid JSON, missing fields, incorrect field types, unsupported environments, and invalid capability parameters. Exact validation rules and error strings are capability-specific; there is no universal schema-validation envelope.
Protected routes authenticate before route-specific body validation. A credential-disclosure route also validates its credential-request identifier before processing the body.
Provider-native responses
The Stripe, Square, PayPal, Twilio, and Telnyx relays can preserve provider responses. Where applicable, Gateman retains:
- The original provider HTTP status.
- The provider response body bytes.
- The selected provider content type.
- Selected provider request or diagnostic headers.
The selected headers differ by adapter. Gateman does not forward every upstream header, and a provider body is not necessarily JSON.
A provider response means the provider returned an HTTP response that Gateman can relay. A Gateman-generated failure means Gateman could not complete part of the request and returned its own controlled response. A provider 400 or 500 is therefore not automatically a Gateman 502.
PayPal obtains an access token before making the requested provider call. Some token acquisition failures are translated into Gateman-controlled responses, so no provider operation response exists to relay in those cases.
Email responses
SES, Postmark, and Resend return Gateman-generated email responses.
In production, a successful accepted send includes fields such as:
{
"ok": true,
"caller": "application-id",
"provider": "resend",
"message_id": "provider-message-id",
"error": null
}
Provider acceptance is not proof of final recipient delivery.
In staging and development, Gateman captures the message in its local mailbox instead of sending through the external provider. A successful capture indicates local capture, not delivery.
Email failures include request validation, missing configuration, definite provider rejection, provider throttling, and ambiguous provider outcomes. When Gateman cannot establish whether the provider accepted the message, it returns 409 with mail_send_indeterminate. The provider may have accepted the message. Do not blindly resend an indeterminate operation.
Email idempotency is optional and capability-specific. A completed successful replay may return its retained result. Reusing a key with a different payload returns 409 with idempotency_key_payload_conflict.
SMS responses
In production, Twilio and Telnyx responses may preserve provider-native statuses and bodies. AWS SMS returns Gateman-generated JSON for successful operations and controlled Gateman errors for failures.
In staging and development, SMS is captured locally. The following is illustrative; the capture ID is not stable:
{
"ok": true,
"staged": true,
"sent": false,
"provider_accepted": false,
"delivery_status": "not_sent",
"message_id": null,
"capture_id": "example-capture-id",
"provider": "twilio",
"environment": "staging"
}
When Gateman cannot establish whether an external send was accepted, it returns 409 with sms_send_indeterminate where the route's idempotency behavior applies. This does not establish that the SMS was rejected or never sent. Do not automatically retry an uncertain send.
Credential retrieval responses
/secrets/get
POST /secrets/get is environment-aware and returns requested values only:
{
"ok": true,
"secrets": {
"EXAMPLE_NAME": "value"
}
}
Unknown, unconfigured, invalid, and retrieval-disabled names are omitted rather than returned as per-entry errors. Custom-secret lookup uses the selected environment slot; it does not disclose both environment slots in one response.
Credential-request replay conflicts return 409. Credential-store or KMS failures after request acceptance may return 500.
/db/get
POST /db/get is environment-independent and retrieves one named database password. A successful response contains the requested name and includes password only when a value is available; an unknown or unavailable named password returns 200 without that property. Credential-request replay conflicts return 409; credential-store failures may return 500.
Both routes require the single-use X-GM-Credential-Request-ID defined by Contract v1. A legitimate subsequent retrieval attempt requires a fresh identifier. Reusing an already-claimed identifier is not a valid retry mechanism.
Other response families
/config/getreturns Gateman-generated public configuration JSON and is non-billable, with its own request rate limit./turnstile/getand/recaptcha/getreturn Gateman-generated credential bundles; Gateman is not in the visitor verification request path./s3/presignand/r2/presignreturn JSON containing a presigned URL, method, expiry, and required headers./client/getreturns app-scoped application/client context JSON./client/php,/client/node,/client/python, and/client/goreturn downloadable source or archive artifacts rather than ordinary JSON API responses.
Idempotency and replay errors
These are separate mechanisms:
Authentication timestamp freshness
Signed requests are accepted only within the Contract v1 freshness window. Freshness does not make every ordinary signed request single-use.
Credential-disclosure protection
/secrets/get and /db/get require single-use credential-request identifiers. Reusing a claimed identifier returns 409.
Operation idempotency
Email and SMS have optional, capability-specific idempotency stores. Completed successful operations may replay a retained result. A different payload under the same key produces an idempotency conflict. Ambiguous sends require special care because external acceptance may be unknown.
Payment-provider idempotency remains provider- and capability-specific. Gateman does not provide universal idempotency for every route.
Concurrency and rate limiting
Gateman applies shared per-application concurrent-request admission. Exhausted application capacity can return 429 with Retry-After: 1; admission infrastructure failure can return 503.
Some routes also have endpoint-specific limits, such as public configuration. Providers may impose independent rate limits, which can be returned as provider-native responses or translated by the capability. There is no universal retry interval or rate-limit header set.
Operation accounting and failed requests
HTTP success or failure alone does not determine whether an operation counts toward Gateman usage.
Authentication rejection, request validation, and some concurrency or admission failures occur before operation reservation. Other failures occur after an operation has been reserved, including provider attempts and capability-specific failures. Staging and development captures remain subject to applicable operation accounting.
Do not treat every 4xx or 5xx as free. Once a billable operation has been accepted into the accounting lifecycle, it consumes one unit whether execution succeeds or fails; rejection before reservation does not consume an operation. See Operation Accounting for the complete rule.
Canonical-client response handling
The four official v1.0.0 clients expose the underlying HTTP result rather than turning every non-2xx status into a language-level exception. Each exposes, using language-appropriate types:
- HTTP status.
- Response headers.
- Raw response body.
- Parsed JSON when possible.
X-Gateman-Request-IDwhen valid.Retry-Afterwhen present and parseable.
Network, configuration, timeout, and transport failures are separate from a received HTTP response. A malformed or binary body may be preserved as raw data while JSON parsing reports a decode problem.
See the PHP client, Node.js client, Python client, and Go client guides for language-specific response types.
Handling responses in an application
- Inspect the HTTP status first.
- Preserve the response body even when it is not JSON.
- Read Gateman
okanderrorfields only when that response actually uses the Gateman-generated JSON convention. - Handle provider-native bodies according to the provider's contract.
- Distinguish a received HTTP response from a network or timeout failure.
- Retain
X-Gateman-Request-IDfor diagnosis. - Respect
Retry-Afterwhen it is provided. - Avoid blind retries when provider acceptance may be ambiguous.
- Never log credentials, secrets, authorization headers, or sensitive provider payloads.
Do not assume every 502 or 503 is safe to retry. The capability contract and whether external acceptance is known determine the next action.
