Generated from Postwind commit a19b2e4 OpenAPI 0.1.0 synced

Errors

Every error answer of the API is a problem details object (RFC 9457) with Content-Type: application/problem+json. MCP tools return the same code and detail; see Tool rules.

The error body

{
  "type": "https://postwind.dev/errors/key_invalid",
  "title": "API key invalid",
  "status": 401,
  "detail": "the API key is not valid",
  "code": "key_invalid"
}
Field Meaning
type https://postwind.dev/errors/<code>
title A short summary, the same for every occurrence of the code
status The HTTP status code
detail What went wrong this time, for people
code The error code, stable; the table below lists every code
errors Every invalid field or unmet precondition, on 422 only; on other statuses the key is left out

Each item of errors is { "field", "code", "message" }, for example { "field": "name", "code": "invalid", "message": "must not be empty" }. The full schema is Problem.

detail never repeats a key string sent in the request and never carries the cause of an internal failure. The cause is in the node’s log under the request’s trace_id.

The type URIs are identifiers, as RFC 9457 allows. Use code in your code; this page is the readable reference for each one.

Status codes

Status Meaning
400 The body is not JSON
401 No key, or the key is not valid
403 The key lacks a scope or a kind the operation needs
404 No such resource or path, or a resource of another project
409 The state of the resource does not allow the operation
413 The body is larger than 10 MB
415 The body is not sent as application/json
422 Validation failed, or preconditions are not met; every violation is listed
429 The key is over its rate limit; Retry-After says when to try again
500 A failure inside Postwind; the cause is in the log
504 The storage did not answer in time; the outcome is unknown

Every 401 carries WWW-Authenticate: Bearer. Every 429 carries Retry-After. A 504 has no Retry-After.

An unknown path under /api/v1 is 404 not_found, answered before the key is checked.

Error codes

code Status When
key_missing 401 No Authorization header, a scheme other than Bearer (case does not matter), or nothing after Bearer
key_invalid 401 Not a key string, no key with that prefix, or a wrong secret. The three cases look the same from outside, so an answer does not tell which prefixes exist
key_revoked 401 The secret is right; the key is revoked or its project is deleted (detail tells which)
key_expired 401 The secret is right; expires_at has passed
forbidden 403 The key lacks the scope or the kind the operation needs, or it grants more than it holds; detail names what is missing
not_found 404 No such resource or path, or a resource of another project. The body is the same in every case and does not repeat the id from the request
conflict 409 The state of the resource does not allow the operation
validation_failed 422 Fields failed their checks; every one is in errors
invalid_json 400 The body is not JSON: bad syntax, not UTF-8, or empty; detail says where it breaks
body_too_large 413 The body is larger than 10 MB, in the API and in MCP
unsupported_media_type 415 The body is not sent as Content-Type: application/json; parameters such as charset are allowed
precondition_failed 422 Preconditions are not met; every one is in errors
rate_limited 429 The key is over its rate limit; Retry-After is in seconds
idempotency_key_reused 422 The Idempotency-Key was used in the last 24 h for another request of this API key: another body, path or method. Nothing was done
idempotency_key_in_progress 409 A request with this Idempotency-Key and this body has not answered yet, here, on another node or at the same time, or it answered 504 storage_timeout. Nothing was done; try again later
storage_timeout 504 The storage did not answer within storage.clickhouse.query_timeout_secs; a change the request made may still take effect
internal 500 A failure on the Postwind side, in the storage; the cause is in the log

The code values are the same in MCP tool errors.

Field codes

On 422, each item of errors names a field and a code. Nested fields are named by path, for example settings.tracking.opens. An error about the whole body has an empty field.

Field code When
unknown_field The operation has no such field, nested ones included (settings.colour)
required A required field is missing
invalid_type The JSON type is wrong, or the field is null and cannot be cleared
invalid The value breaks the rule of the field: a pattern, a length, the syntax of an email address
out_of_range A number is outside the allowed range
unknown_relay relay:<name> is not declared in the node’s config
transport_unavailable Only in precondition_failed, field transport: the node has no adapter for the project’s transport. available_transports in GET /api/v1/server lists those it has
domain_not_verified Only in precondition_failed of a launch, field from_email: the sender’s domain is not a sending domain of the project, or it is not verified
no_recipients Only in precondition_failed of a launch, on the campaign’s lists: no recipients are left after deduplication and suppression
quota_exceeded Only in precondition_failed of a launch, field settings.daily_send_quota: there are more recipients than the project’s daily quota has left

Form errors come first. A body with errors of form and of values gets the form errors only; after the form is fixed, the values are checked, and again every violation comes back at once. The full order of checks is on Request bodies.

What a 504 means

504 storage_timeout means that the outcome is unknown, not that nothing happened. A write that the node stopped waiting for can still reach the storage later.

  • GET can be repeated at any time.
  • PATCH, DELETE and revoking a key can be repeated: the same call gives the same result. A revoked key answers 204 again.
  • A POST without Idempotency-Key is checked first. A project that was created makes the repeat fail with 409 conflict on its slug. A key that was created makes the repeat create a second key, and nobody has the first key string: find it by name and created_at in the key list and revoke it.
  • A POST with Idempotency-Key never runs twice: the repeat gets 409 idempotency_key_in_progress until the key’s 24 h end. Check whether the resource exists, then go on with a new key.

A request whose storage call hangs answers no later than twice the time limit after its usual time: 10 s with the default of 5 s. A client timeout shorter than that can cut off an answer, including a 201 with the string of a new key.