DocumentationErrors & retries

Errors & retries

v1

Every error has a code, a message, and a request ID. Handle the uncertainty as carefully as the happy path.

A consistent error format

JSON
{
  "error": {
    "code": "validation_error",
    "message": "Choose at least one account."
  },
  "request_id": "req_example"
}

Use the stable code for program logic. The message is intended for diagnosis. Store the request ID with your own logs; do not log bearer keys or connected-account credentials.

HTTPMeaningAction
400Invalid input or unsupported optionCorrect the request using error details
401Missing, invalid, or revoked keyCheck the key and environment
403Insufficient scope or account capabilityGrant the required scope or reconnect the account
404Resource not found in this workspace/modeCheck ID and key environment
409Conflicting state, idempotency payload, or a plan’s account limitInspect the existing post; for account errors, disconnect an account or upgrade
429Per-minute rate or monthly allowance reachedCheck the code: wait for Retry-After, or until reset_at for an allowance
5xxTemporary service or platform failureRetry safe operations with backoff

Plan limit errors

These codes mean a plan limit was reached. Rejected new work incurs no charge. Previously accepted posts remain available for status checks, exact retries and cancellation; a delivery may already have reached a platform. Plans and limits lists every field.

CodeHTTPWhenWhat to do
usage_limit_exceeded429A monthly allowance would be exceededWait until details.reset_at or upgrade. Never retry sooner.
rate_limit_exceeded429The per-minute request rate was reachedWait for Retry-After, then retry
account_limit_reached409Connecting one more account would exceed the planDisconnect an account or upgrade
account_capacity_exceeded409More accounts are connected than the plan allows, for example after a downgradeDisconnect accounts or upgrade. Nothing is deleted.

Idempotency is part of the request

Choose a unique idempotency key for each logical post. Use that same key and the same payload after a timeout. Reusing a key with a different payload is a conflict; use a new key only for a genuinely new post.

TypeScript
try {
  await castrook.posts.create(payload, {
    idempotencyKey: 'campaign-42-post-7',
  });
} catch (error) {
  if (error instanceof CastrookError) {
    console.error(error.code, error.requestId);
  }
}

An uncertain result needs inspection

A platform can accept a write before a network timeout. Castrook does not blindly repeat an ambiguous publish. Inspect the target error and the platform account before creating a replacement. The same caution applies to comment replies, which are not idempotent.

Retry safe operations

The TypeScript client retries GET requests and posts with idempotency keys on transport failures, rate_limit_exceeded, and 5xx. It waits for a Retry-After of up to 30 seconds and uses a request timeout. It never retries usage_limit_exceeded or account limit errors, and it does not automatically repeat comment replies.

Do not treat HTTP 202, an upload ID, or a platform processing receipt as proof of public publication. Check the final destination state and visibility.