Skip to content

Errors

Every error response follows the same envelope:

{
"code": "...",
"type": "https://docs.ampout.dev/errors/<code>",
"message": "...",
"details": { ... }
}

code is the only thing your client should branch on — message is human-readable and may change. The HTTP status is determined by the code; both are documented below.

CodeStatusFires when
unauthorized401Missing, malformed, or revoked API key.
not_found404Generic fallback for resources without a dedicated code.
validation_failed422Model validation failed. message carries the validator’s full message.
unprocessable422Request was syntactically valid but couldn’t be processed (e.g., business-rule violation).
bad_request400Malformed request.
rate_limited429Exceeded the per-key (60/min) or per-IP signup throttle. Includes Retry-After.

These all return 404. They’re semantically equivalent to not_found but emit distinct codes so your client can branch precisely.

CodeResource
campaign_not_foundCampaign
contact_not_foundContact
contact_import_not_foundContactImport
outreach_not_foundOutreach
smtp_credential_not_foundSmtpCredential
api_key_not_foundApiKey
webhook_not_foundWebhook
plan_not_foundPlan
CodeStatusFires when
no_pending_enrollments422POST /campaigns/:id/dispatch and there’s nothing to dispatch.
campaign_not_active422POST /campaigns/:id/pause on a non-active campaign.
campaign_not_paused422POST /campaigns/:id/resume on a non-paused campaign.
contact_already_enrolled422POST /campaigns/:id/enroll_contact and the contact already has an enrollment.
no_emails_provided422POST /campaigns/:id/test_send without any emails.
no_contacts_provided422POST /contact_imports without any contacts.
CodeStatusFires when
plan_limit_reached402Account has reached its plan’s monthly send limit. details includes current_period_sends, monthly_limit, plan_slug.
plan_required402(Reserved for paid-only features in the future.)
feature_not_in_plan402(Reserved for paid-only features in the future.)
plan_not_purchasable422POST /billing/checkout for a free or private plan.
no_stripe_customer422POST /billing/portal or /checkout and the account has no Stripe customer (signed up before Stripe was wired).
CodeStatusFires when
cannot_revoke_last_key422DELETE /api_keys/:id would leave the account with zero active keys.
CodeStatusFires when
invalid_signature401POST /webhooks/stripe HMAC signature verification fails. (Inbound only — your outbound webhook receiver is responsible for its own signature checks.)
CodeStatusFires when
idempotency_conflict409Same Idempotency-Key reused with a different request body.
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"code": "campaign_not_found",
"type": "https://docs.ampout.dev/errors/campaign_not_found",
"message": "Campaign not found"
}

422 (validation failed) — note the dynamic message

Section titled “422 (validation failed) — note the dynamic message”
HTTP/1.1 422 Unprocessable Content
Content-Type: application/json
{
"code": "validation_failed",
"type": "https://docs.ampout.dev/errors/validation_failed",
"message": "Name can't be blank, Min wait seconds must be greater than or equal to 0"
}
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"code": "plan_limit_reached",
"type": "https://docs.ampout.dev/errors/plan_limit_reached",
"message": "Plan send limit reached for the current period",
"details": {
"current_period_sends": 5000,
"monthly_limit": 5000,
"plan_slug": "hobby"
}
}
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
{
"code": "rate_limited",
"type": "https://docs.ampout.dev/errors/rate_limited",
"message": "Too many requests. Slow down and retry after the period resets.",
"details": { "limit": 60, "period": 60 }
}