Skip to content

Errors

PostStack uses standard HTTP status codes and returns JSON error responses. All error responses follow the same format.

Error Response Format

All error responses include an error field with a human-readable message and a stable, machine-readable code you can branch on (the wording may change; the code will not):

json
{
  "error": "Domain not found",
  "code": "not_found"
}

The SDK throws typed errors that you can catch and inspect:

typescript
import { PostStackError } from '@poststack.dev/sdk';

try {
  await poststack.emails.send({ ... });
} catch (err) {
  if (err instanceof PostStackError) {
    // err.statusCode = 422
    // err.code       = "unprocessable_entity"
    // err.requestId  = "03f82111-…" (quote it to support)
    // err.message    = "Domain 'example.com' is not verified"
  }
}

HTTP Status Codes

StatusMeaningDescriptionCode
200OKRequest succeeded. Response body contains the result.—
201CreatedResource created successfully.—
202AcceptedRequest accepted and queued asynchronously. Returned by POST /emails and POST /emails/batch — the body contains the new id(s) but delivery happens out-of-band.—
400Bad RequestThe request body is malformed, missing required fields, or fails validation (bad email address, too many recipients, batch over 100).invalid_request
401UnauthorizedMissing or invalid API key.unauthorized
403ForbiddenAPI key does not have permission for this action.forbidden
404Not FoundThe requested resource does not exist.not_found
409ConflictThe resource already exists (e.g., duplicate domain or contact email).conflict
413Payload Too LargeThe request body or an attachment exceeds the size limit.payload_too_large
422Unprocessable EntityThe request is well-formed but cannot be processed — e.g. the from domain is not verified, or the template is not published.unprocessable_entity
429Too Many RequestsRate limit exceeded. Check the Retry-After header.rate_limit_exceeded
500Internal Server ErrorSomething went wrong on our end. Try again later.internal_error
503Service UnavailableTemporarily unavailable (maintenance or a dependency is down). Retry with backoff.service_unavailable

Common Errors

Here are common error messages and how to resolve them:

ErrorStatusResolution
Invalid API key401Check that your API key is correct and starts with sk_live_ or sk_test_.
API key does not have permission403Use a key with full_access permission, or upgrade the key permissions.
Domain 'example.com' is not verified422Verify your domain DNS records before sending. Use POST /domains/:id/verify.
Domain 'example.com' not found for this team422The from address uses a domain you have not added. Add it first with POST /domains.
Contact already exists409A contact with this email already exists. Use PATCH to update instead.
Rate limit exceeded429Wait and retry after the duration specified in the Retry-After header.
emails: Too big: expected array to have <=100 items400Reduce the batch to 100 emails or fewer per request.
Template is not published422Publish the template with POST /templates/:id/publish before using it.
Request body is missing required fields: to400Include the "to" field as an array of email addresses.
to.0: must be a valid email address400Check the email address format in the "from" or "to" fields.

Rate Limits

Rate-limited endpoints (sending, and most create/update routes) carry three headers describing your current bucket so you can back off proactively instead of waiting for a 429. Limits are counted per API key and per endpoint:

HeaderMeaning
X-RateLimit-LimitMaximum requests allowed in the current window.
X-RateLimit-RemainingRequests still available before throttling kicks in.
X-RateLimit-ResetUnix epoch seconds when the current window resets.
Retry-AfterOnly set on 429 responses — seconds to wait before retrying.

When you exceed the limit the API returns a 429 with the same X-RateLimit-* headers plus Retry-After:

bash
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1748430120
Retry-After: 47
Content-Type: application/json

{
  "error": "Rate limit exceeded. Please try again later.",
  "code": "rate_limit_exceeded"
}

The SDK automatically retries 408, 429 and 5xx responses (and network errors) with exponential backoff — up to 3 retries by default, configurable with the maxRetries option.

Related