API errors
Every error from the Pluto Ads API is application/problem+json (RFC 9457). Its type is a link to
one section of this page, named after the stable code. The title is written for the person or agent
reading it and never contains provider or database internals.
{"type":"https://docs.plutoads.ai/api/errors#invalid","title":"The request has 1 problem.","status":422,"code":"invalid","errors":[{"field":"name","message":"Use a name of 1 to 120 characters."}]}Retry a write with the same Idempotency-Key whenever you retry it at all. The stored result comes
back and nothing runs twice. See Idempotency keys.
bad_request
Status 400. The request can't be read: malformed JSON, a wrong type (a JSON number where an amount
belongs), a missing or malformed header such as Pluto-Workspace, or an invalid path or query value.
The title names the problem. Fix the request; sending it again unchanged fails the same way.
unauthenticated
Status 401. There is no credential, or it is invalid, expired or revoked. Keys and agent clients stop
working on the first request after they are revoked. Get a new credential: create a key on the API page
of the app, or exchange your agent client's credentials for a new token. See
Authentication.
forbidden
Status 403. The credential is valid but can't do this. Either it lacks the permission (review,
write, publish, admin or organization), or it names a workspace it doesn't belong to. The title says which. Ask a
workspace admin for the permission, or use a credential of that workspace.
not_found
Status 404. The item isn't in this workspace. It may have been deleted, or the ID belongs to another
workspace: the API never reveals whether an ID exists elsewhere. Check the ID and the workspace the
credential belongs to.
conflict
Status 409. The request is well formed but can't apply to the current state. The problem body carries
what you need to resolve it:
| Cause | Extra field | What to do |
|---|---|---|
A stale expectedVersion (someone changed the same field), on launches, ad sets, ads, media, folders and settings | current | Merge with current, retry with expectedVersion set to current.version |
An Idempotency-Key or commandId reused for a different request | reason: command_id_reused | Use a new key for new work |
| A launch that isn't ready to publish, or an approval that no longer matches it | items | Fix the checklist, then approve again |
| Pending changes that changed since you reviewed them | revisionsVersion | Read GET /launches/{id}/revisions again and resend with that version |
| Pending changes Meta can't take | problems or blocked | Fix the listed fields, or discard the changes Meta can't apply |
| A live change to a value that stays managed in Meta (imported launches) | managedInMeta | Change it in Meta Ads Manager |
| A review status still in use | usageCount | Move its ads to another status first |
| A targeting, audience or ad preview request without a Meta connection | reason: meta_disconnected | An admin connects Meta in Integrations |
| A targeting, audience or ad preview request after Meta access expired or was revoked | reason: meta_reauthorize | An admin reconnects Meta in Integrations |
| A targeting change that makes an ad set reach the EU without the advertiser names Meta requires | reason: eu_advertiser_required, missing, fix | Set the beneficiary and payer names (fix links to where), then try again |
| An ad preview Meta can't render yet | reason: not_previewable, missing | Add what missing lists (or read metaMessage when Meta refused it) |
| Deleting media a launch that isn't published yet still uses | reason: used_in_unpublished_launch, launches | Remove it from those launches first, or archive the media instead |
| A comment action the Meta connection lacks permissions for | reason: meta_permissions, missingPermissions | An admin reconnects Meta and grants the listed permissions |
| A profit read without a connected Pluto Profit store | reason: pluto_profit_not_connected | An admin connects Pluto Profit in Integrations |
| A profit read after Pluto Profit stopped accepting the workspace's approval (removed or expired, or the person who approved lost the store) or key (revoked) | reason: pluto_profit_reauthorize | An admin reconnects Pluto Profit in Integrations (or reconnect_pluto_profit); statusReason on the connection says why |
| A Slack action without a connected Slack workspace | reason: slack_not_connected | An admin connects Slack in Integrations |
| A Slack action after Pluto Ads was removed from the Slack workspace, or its access was revoked | reason: slack_reauthorize | An admin reconnects Slack in Integrations |
| A Slack message to a channel the workspace hasn't connected | reason: slack_channel_not_connected | An admin connects the channel in Integrations, or post to a connected one |
| A Slack message to a connected channel Slack no longer lets Pluto Ads post in (archived, deleted, or the app was removed from it) | reason: slack_channel_unavailable | Restore the channel or invite Pluto Ads again in Slack, or connect another channel |
| A retry of a Slack message whose first attempt may already have been posted (Slack didn't answer in time) | reason: slack_message_outcome_unknown | Check the channel in Slack; send again with a new idempotency key only if the message isn't there |
| A change to an archived ad or ad set, a draft-only action on something live, or a job with nothing to retry | None | Read title; restore the item or use the live controls |
| A plan change or checkout the organization doesn't fit: more workspaces than the plan holds (Pro holds 5, so Business to Pro needs 5 or fewer) | reason: too_many_workspaces, limit, used, nextAction: archive_workspaces, upgradeTo (the plan that fits) | Archive workspaces or move them to another organization (nothing is deleted), then try again; or choose the plan in upgradeTo |
| A plan change on an Enterprise plan | reason: contact_sales, salesUrl, contactUrl | Talk to sales: book a call at salesUrl |
Cancelling an Enterprise agreement before its end date (POST /organizations/{id}/billing/portal with flow: cancel) | reason: agreement_term, runsUntil (the end date), nextAction: contact_sales, salesUrl, contactUrl | The agreement runs until runsUntil; talk to your account manager about the renewal |
| Changing the plan of an organization that doesn't subscribe yet | reason: not_subscribed | Start a checkout with POST /organizations/{id}/billing/checkout instead |
| Moving to a larger plan and to monthly billing in one request | reason: one_change_at_a_time | Upgrade first, then switch to monthly billing |
| Starting the free trial for an organization that already had one | reason: trial_unavailable | Choose a plan |
| Choosing a plan while the organization is scheduled for deletion | reason: organization_deleting | Restore the organization first |
| Turning on extra storage without a subscription | reason: subscription_required | Choose a plan first |
| A checkout for an organization that already subscribes | reason: subscribed | Change the plan with POST /organizations/{id}/billing/plan instead |
| A checkout while another checkout for the organization is still being prepared | reason: checkout_in_progress | Wait a moment, then try again; a retry with the same idempotency key continues the same checkout |
| A checkout that ended before it opened (it expired, or another checkout replaced it) | reason: checkout_ended | Start a new checkout with a new idempotency key |
| Turning on on-demand usage without a paid plan: during the trial, without a plan, or with complimentary access (there's no card) | reason: trial, no_subscription or complimentary | Choose a plan first (for complimentary, talk to sales for more); turning it off always works |
| Archiving or deleting the organization's last active workspace, or a workspace that's already in that state | None | Read title; to close everything, an owner deletes the organization |
| Restoring an organization after deleting its data has started | reason: purge_started | None: it can't be restored any more |
| Deleting your account while you're the only owner of an organization | reason: sole_owner, organizations | Make someone else an owner of each listed organization, then try again |
| Transferring ownership to someone who isn't an admin of the organization | None | Make them an admin first |
invalid
Status 422. One or more fields break a rule. errors lists every problem at once, each with a field
path (such as adSets/0/name) and a message. Fix every listed field before you send it again.
rate_limited
Status 429. The credential or its workspace is over its request budget. The Retry-After header says
how many seconds to wait. Wait that long, then retry with the same idempotency key. See
Rate limits. Slack messages get it too when a channel's next posting slot is
more than a few seconds away, or when Slack itself asks us to slow down.
payment_required
Status 402. The organization has no access right now, so it's read-only and nothing was changed. accessReason
says why: no_subscription, trial_ended (the free trial ended without a subscription), canceled, unpaid,
past_due after its grace period, incomplete, paused, stale (a renewal couldn't be confirmed) or
complimentary_ended (complimentary access ended and the days to choose a plan passed). feature names what was
refused, for example writes or publishing. upgradeUrl is the organization's Plan & billing page: an owner
or admin subscribes there (or with start_checkout), or updates the payment method when a payment failed.
A read-only organization can still do the same things in the app, with API keys and through MCP:
- Read everything, including exports (a workspace's ads as CSV, its media as a ZIP, your account's data).
- Pause campaigns, ad sets and ads, and lower budgets. Resuming and raising are refused.
- Turn a loop off, archive it, or delete a draft that never ran (
DELETE /loops/{id}). - Approve a change set that only pauses or lowers budgets, reject any change set, and undo a change
(
POST /change-sets/{id}/items/{key}/undo,POST /loop-runs/{id}/undo) when the undo pauses or lowers a budget. - Cancel a publish, a queued job or a loop run; revoke API keys, agent clients, agent connections and invitations; delete webhooks; disconnect Meta, Slack and Pluto Profit.
- Archive or delete workspaces, delete or restore the organization, remove members and leave.
- Use the account routes and billing (the billing routes and tools), so you can always read the state and hand the link to a person.
Everything else answers payment_required. Manual refreshes such as sync_leads or a comment sync are refused
too; background syncs from Meta keep running. Queued work that isn't on this list stops when it runs: the job item fails with
"This organization is read-only until it has a plan.", and isn't run again once the organization pays. Loops don't
run, and webhook deliveries are recorded as not sent. Offline changes uploaded through the sync queue are refused
one by one (rejected, with this message as reason), except your own preferences and inbox. See
Billing.
{"type":"https://docs.plutoads.ai/api/errors#payment_required","title":"The free trial ended, so this organization is read-only. An organization owner or admin can choose a plan in Organization settings, Plan & billing.","status":402,"code":"payment_required","feature":"writes","accessReason":"trial_ended","upgradeUrl":"https://app.plutoads.ai/settings/organization/billing"}plan_limit
Status 402. The organization's plan doesn't include this, so it didn't start and nothing was changed.
feature says what, plan is the organization's plan (pro, business, enterprise, or null during the
trial), and nextAction is the one next step: upgrade (to the plan in upgradeTo, from upgradeUrl),
contact_sales (book a call with sales at salesUrl; contactUrl is the contact page), keep_plan, or for
media storage enable_extra_storage or
raise_storage_limit. upgradeTo is never smaller than what the organization needs: Pro when its workspaces fit
Pro (up to 5) and Pro costs no more than Business for its members, otherwise Business. Owners and admins change the plan in Plan & billing, or with
POST /organizations/{id}/billing/plan.
feature | Meaning | Next step |
|---|---|---|
workspaces | Creating or restoring a workspace past what the plan holds (limit, used). reason is trial (the free trial holds 1), plan_cap (Pro holds 5, and complimentary access may cover a set number; Business and Enterprise have no cap, and on Enterprise workspaces beyond the agreement are added to the next invoice) or scheduled_plan_cap (a move to a smaller plan is scheduled for the end of the period, and limit is what that plan holds) | For trial, upgrade to Pro. For plan_cap, upgrade to Business, or contact_sales with complimentary access. For scheduled_plan_cap, keep_plan: cancel the scheduled change with DELETE /organizations/{id}/billing/plan, then add the workspace |
members | Inviting, adding or reactivating a member past what complimentary access covers (limit, used; pending member invitations count, guests never do). An invitation accepted once it's full stays pending until there's room | contact_sales: talk to sales at salesUrl |
workspace_limits | Setting a monthly limit per workspace comes with Business. Removing one (limit: null) works on every plan | upgrade to Business |
usage_breakdown | Usage by workspace and by connected agent come with Business: by=workspace and by=agent, the workspaceId and agentId filters, GET /workspace/usage (get_workspace_usage) and the CSV export. Usage by member, source, loop and model works on every plan | upgrade to Business |
media_storage | The organization's media storage is full (limit, used in GB, decimal strings). reason is extra_storage_off (nextAction enable_extra_storage), storage_limit (extra storage reached its monthly limit; nextAction raise_storage_limit) or extra_storage_unavailable (during the trial: nextAction upgrade), with raisedBy: organization_admin | An owner or admin turns on or raises extra storage in Spending, or with PUT /organizations/{id}/billing/storage; uploads continue then |
Pluto Ads never deletes anything to make room. Retrying unchanged fails the same way.
{"type":"https://docs.plutoads.ai/api/errors#plan_limit","title":"Pro holds up to 5 workspaces. Upgrade to Business to add more.","status":402,"code":"plan_limit","feature":"workspaces","reason":"plan_cap","limit":5,"used":5,"plan":"pro","nextAction":"upgrade","upgradeTo":"business","upgradeUrl":"https://app.plutoads.ai/settings/organization/billing","salesUrl":"https://cal.com/plutocamp/pluto-ads-sales","contactUrl":"https://plutoads.ai/contact"}spend_limit
Status 402. The Agent (or an AI step in a loop) reached a usage limit, so it didn't start this work and
nothing was changed. The first limit reached stops it; reason names it:
reason | Meaning | What to do |
|---|---|---|
trial_cap | The free trial's AI usage is used | An owner or admin chooses a plan |
included_used | The organization's included usage is used up and usage beyond it is off | An owner or admin turns on usage beyond what's included, or wait for resetsAt |
spend_limit | Usage beyond what's included reached the organization's monthly limit | An owner or admin raises the limit, or wait for resetsAt |
workspace_limit | This workspace reached the monthly limit set for it (targetId, targetName) | An owner or admin raises or removes the workspace's limit, or wait for resetsAt |
member_limit | This person reached the monthly limit set for them (targetId, targetName) | An owner or admin raises or removes their limit, or wait for resetsAt |
complimentary_used | The organization has complimentary access, and this month's included AI usage is used. There's no usage beyond it (raisedBy: support, salesUrl, contactUrl) | Wait for resetsAt, the 1st of next month (UTC), or talk to sales at salesUrl for more |
limit and used are decimal strings in currency (usd), resetsAt is when usage starts again from zero
(the end of the billing period), raisedBy is who can change it (organization_admin), and usageUrl is the
organization's Spending page, where owners and admins change limits (or set_usage_limit,
PUT /organizations/{id}/usage/limits/..., PUT /organizations/{id}/billing/on-demand); upgradeUrl is Plan &
billing. Retrying before a limit changes fails the same way. Everything that doesn't use AI keeps working. See
Billing.
{"type":"https://docs.plutoads.ai/api/errors#spend_limit","title":"This organization used the AI usage its members include for this billing period. An organization owner or admin can turn on on-demand usage in Organization settings, Spending; included usage resets on October 26.","status":402,"code":"spend_limit","reason":"included_used","limit":"100.00","used":"100.00","currency":"usd","resetsAt":"2026-10-26T00:00:00Z","onDemandEnabled":false,"raisedBy":"organization_admin","upgradeUrl":"https://app.plutoads.ai/settings/organization/billing","usageUrl":"https://app.plutoads.ai/settings/organization/spending"}workspace_archived
Status 409. The workspace is archived, so it can be read but not changed. Every write refuses the same
way, whatever the credential. workspaceId and organizationId name it. An organization owner or admin
restores it with POST /organizations/{id}/workspaces/{workspaceId}/restore (or restore_workspace), or
in the app under Organization settings, Workspaces. See
Archiving and deleting.
{"type":"https://docs.plutoads.ai/api/errors#workspace_archived","title":"This workspace is archived, so it can be viewed but not changed. An organization owner or admin can restore it.","status":409,"code":"workspace_archived","workspaceId":"0192c9a4-7c1e-7b6e-9a51-3f1d2c4b5a60","organizationId":"0192c9a4-7c1e-7b6e-9a51-3f1d2c4b5a61"}workspace_deleted
Status 410. The workspace is scheduled for deletion. Nobody can open it and its credentials were
revoked. purgeAfter is when its data and media are deleted for good; until then an organization owner or
admin can restore it. After that date the workspace is gone and its ID answers 404.
{"type":"https://docs.plutoads.ai/api/errors#workspace_deleted","title":"This workspace is scheduled for deletion on October 26, 2026. An organization owner or admin can restore it until then.","status":410,"code":"workspace_deleted","workspaceId":"0192c9a4-7c1e-7b6e-9a51-3f1d2c4b5a60","organizationId":"0192c9a4-7c1e-7b6e-9a51-3f1d2c4b5a61","purgeAfter":"2026-10-26T09:41:12Z"}organization_deleted
Status 410. The workspace's organization is scheduled for deletion, with every workspace in it.
purgeAfter is the date. Until then an organization owner, signed in to the app, can restore it.
member_suspended
Status 403. An organization owner or admin suspended your access to organizationName
(organizationId). Your work stays; ask them to reactivate you. API keys and agents you had there were
revoked and don't come back on reactivation.
member_removed
Status 403. You were removed from organizationName (organizationId). Your work there stays. You can
still use your other organizations, and an owner or admin can invite you again.
unavailable
Status 503. A service the API depends on is down or not configured. Nothing was changed. Retry later
with the same idempotency key.
internal
Status 500. Something failed on our side. We log it with the request's x-request-id. Retry with the
same idempotency key, and include the x-request-id response header when you report it.