Ticketing API / Errors
API error reference
Every error the v1 API returns has a stable error.code. Branch on that, never on error.message, which is written for humans and can change. Each error response also carries a doc_url that links straight to its section on this page, and a request_id worth quoting to support.
{
"error": {
"code": "missing_venue_contact",
"message": "Add a contact email for attendees before publishing",
"request_id": "req_01J...",
"doc_url": "https://equaticket.com/ticketing-api/errors#missing_venue_contact"
}
}All codes
Authentication
missing_auth
HTTP 401No Authorization header was sent, or it was not a Bearer token.
What to do: Send Authorization: Bearer sk_live_... (or sk_test_...) on every request. This is not a retryable condition: retrying the same request without the header returns the same error.
invalid_api_key
HTTP 401The token was not in the sk_live_ / sk_test_ format, or no API key matching it exists.
What to do: Check the key was copied whole and has no trailing whitespace. Keys are shown once at creation; if you no longer have it, create a new key in Settings and revoke the old one.
key_revoked
HTTP 401The API key exists but has been revoked.
What to do: Create a new key in Settings and update your integration. Revocation is permanent, so a revoked key cannot be reinstated.
Authorization
insufficient_scope
HTTP 403The key authenticated, but does not carry the scope this endpoint requires. The response includes a required field naming the missing scope.
What to do: Read error.required, then create a key with that scope. Scopes are fixed at creation, so an existing key cannot be widened.
csrf_error
HTTP 403A state-changing request failed the Origin/Referer check. This affects browser/session-authenticated calls, not Bearer-token API calls.
What to do: Server-to-server integrations should use an API key rather than forwarding browser cookies. If you are calling from a browser, ensure the request originates from an allowed origin.
subscription_blocked
HTTP 403The organization's subscription is past due, paused, or otherwise not in good standing.
What to do: Resolve billing in the dashboard. Writes stay blocked until the subscription is active again. Reads are unaffected.
capacity_exceeds_plan
HTTP 403The event's total capacity is larger than the current plan allows.
What to do: Reduce capacity_total or the ticket type quantities, or upgrade the plan. The message names the applicable limit.
ticket_limit_reached
HTTP 403The organization has reached its plan's ticket allowance for the current window.
What to do: Upgrade the plan, or wait for the rolling window to refresh. The message states the limit and the window length.
free_paid_allowance_exceeded
HTTP 403On the Free plan, the change would take paid tickets sold and kept plus unsold paid tickets on sale past the plan's paid ticket allowance. Returned by publish and by any ticket type or event change that adds paid tickets on sale.
What to do: Lower another event's paid ticket quantities, or move to a paid plan. error.details carries sold, listed, requested, remaining and allowance. Events already on sale keep selling.
capacity_limit_exceeded
HTTP 400On the Free plan, the change would raise an event's total tickets across all ticket types above the per-event limit.
What to do: Lower another ticket type's quantity on the same event, or move to a paid plan. A change that lowers the total, or leaves it unchanged, is never refused.
import_placeholder_not_sellable
HTTP 400On the Free plan, the change would put the ticket type that holds imported tickets on sale (a price, a door price, unhiding it, or an access code).
What to do: Create a new ticket type to sell more tickets. The imported tickets are unaffected.
timed_entry_disabled
HTTP 403A timed-entry (series/slot) endpoint was called while the FEATURE_TIMED_ENTRY flag is off.
What to do: This is a platform-level flag, not an account setting. Contact support if you need timed entry enabled.
terms_acceptance_required
HTTP 403Publishing was attempted before an owner or admin accepted the current platform documents.
What to do: Have an owner or admin sign in and accept the updated terms. API keys authenticate an organization rather than a person, so this cannot be cleared over the API.
email_not_verified
HTTP 403A Free-tier organizer attempted to publish before verifying their email address.
What to do: Verify the address from the confirmation email. This check is skipped for API-key callers, so you will normally see it only on dashboard-authenticated calls.
Request validation
validation_error
HTTP 400 / 422The request body failed schema validation. error.details carries a per-field array of { field, message }, and error.message repeats the first one. Returned as 422 by the shared apiValidationError helper; one public slots endpoint returns 400.
What to do: Read error.details rather than error.message, which is only the first issue. Fix every listed field before retrying, since validation is not incremental.
invalid_json
HTTP 400The request body was not parseable JSON.
What to do: Check for a truncated body, a missing Content-Type: application/json, or double-encoded JSON (a JSON string containing JSON).
invalid_param
HTTP 400A query-string parameter was malformed, such as a non-integer limit or an unparseable date filter. error.param names it.
What to do: Read error.param and correct that one parameter.
missing_param
HTTP 400A required query-string parameter was absent. error.param names it.
What to do: Supply the named parameter. See the endpoint reference for which are required.
invalid_cursor
HTTP 400The cursor pagination value was not a cursor this endpoint issued, or was corrupted in transit.
What to do: Do not construct or edit cursors. Pass back the next_cursor from the previous page verbatim, and restart from the first page if you no longer have it.
invalid_url
HTTP 400A supplied URL failed validation. For webhook endpoints this includes the SSRF guard, which rejects private, loopback and link-local addresses.
What to do: Use a publicly resolvable HTTPS URL. Localhost and private-range addresses are rejected by design; use a tunnel during development.
limit_exceeded
HTTP 400A per-organization cap was reached. Webhook subscriptions are capped at 5 per organization.
What to do: Delete a subscription you no longer need before creating another.
location_mode_conflict
HTTP 422Venue fields (venue_name, venue_address, city, state_province, country) were sent for an event whose location is online or tba.
What to do: These are rejected rather than silently dropped, so a 200 never hides a write that did not happen. Omit them, or move the event back to a venue location first. Note venue_contact_email is NOT a venue field: it is the attendee contact address, is required in every location mode, and is always accepted.
Not found
not_found
HTTP 404The addressed resource does not exist, or belongs to a different organization, or does not match the key's mode.
What to do: Confirm the id and that you are using the right key. A live key cannot see test-mode data and a test key cannot see live data, which reads as a 404 rather than a 403 so that ids cannot be probed across modes.
resource_not_found
HTTP 404A nested resource in the path does not exist under its parent, such as a ticket type id that is not on the given event.
What to do: Verify the parent and child ids belong together. Both are checked, so a valid child under the wrong parent still 404s.
Conflicts
has_active_holds
HTTP 409A ticket type was deleted while buyers were mid-checkout holding some of its quantity, or it changed (a sale or a new hold) while the delete was running.
What to do: Wait for the open checkouts to finish or expire (up to 30 minutes), then retry the delete. Nothing was changed.
currency_locked
HTTP 409currency was changed on an event that already has ticket types or sold tickets.
What to do: Currency is fixed once ticket types exist. Delete the ticket types (only possible with no sales) or create a new event in the other currency.
conflict
HTTP 409The event's status changed between the publish check and the publish write, for example it was paused or cancelled by another request.
What to do: Nothing was published. Re-read the event and, if it is still publishable, retry the publish.
duplicate_slug
HTTP 409An event with this slug already exists in the organization.
What to do: Slugs are unique per organization and permanently locked once an event is published or paused. Choose another slug, or duplicate the event to get a new URL.
event_not_editable
HTTP 409The event is cancelled, completed, or archived, none of which accept updates.
What to do: This is terminal for that event. Duplicate it if you need an editable copy.
field_locked
HTTP 409A field that is locked in the event's current status was modified. On a published or paused event that covers title, starts_at, ends_at, slug, currency and timezone.
What to do: Pause the event first: pausing is the unpublish step and unlocks the date fields. slug stays locked on both published and paused events. Sending a field's existing value is not a change and never 409s.
has_sales
HTTP 409A ticket type with completed sales was deleted, or its price was changed after the first sale.
What to do: Sold ticket types cannot be deleted or repriced, because issued tickets reference them. Reduce the quantity to stop further sales (the floor is quantity_sold + quantity_reserved) and create a new ticket type at the new price.
invalid_transition
HTTP 409The requested status change is not legal from the event's current status.
What to do: Check the current status before acting. Publishing applies to draft and paused events only, and cancelling applies to published events only.
Idempotency
invalid_idempotency_key
HTTP 400The Idempotency-Key header was longer than 256 characters.
What to do: Use a UUID v4. Keys are scoped per API key and expire after 24 hours.
idempotency_key_reuse
HTTP 422This Idempotency-Key was already used for a request with a different method, path, or body.
What to do: Generate a fresh key for a different operation. Reuse is refused rather than served from cache, so a key cannot silently return another request's response.
idempotency_key_in_progress
HTTP 409A request with this key is being processed right now.
What to do: Wait briefly and retry the identical request; the completed response will replay. The in-progress lock expires after 60 seconds, so a crashed request cannot wedge a key permanently.
Rate limiting
rate_limited
HTTP 429The plan's per-minute request limit was exceeded. Limits run from 60 requests/min on Free to 600/min on Pro.
What to do: Back off and retry using the Retry-After and X-RateLimit-Reset response headers rather than a fixed sleep. Authenticated API rate limiting fails closed, so this can also appear during a rate-limiter outage; treat it as retryable either way.
Publishing
missing_venue_contact
HTTP 422The event has no venue_contact_email. It is required to publish in ALL THREE location modes, including online and to-be-announced.
What to do: Set venue_contact_email on the event, via POST /api/v1/events or PATCH /api/v1/events/{id}, then publish. Despite the name it is not a venue field: it is the contact address for attendees and the reply-to on ticket emails.
unsupported_currency
HTTP 422The event currency is not one the connected Stripe account supports. The message lists the currencies that are supported.
What to do: Change the event currency to a supported one before publishing, or connect a Stripe account that supports it. This most often affects organizers outside the United States.
no_ticket_types
HTTP 422The event has no non-archived ticket type.
What to do: Create at least one ticket type via POST /api/v1/events/{id}/ticket-types.
no_payment_account
HTTP 422The event has paid ticket types but no connected Stripe account.
What to do: Complete Stripe Connect onboarding in the dashboard. Free events do not require it, so this check does not apply to them.
payment_account_capabilities_pending
HTTP 422Stripe is still enabling charges or payouts on the connected account.
What to do: Wait for Stripe to finish verification and retry. This usually clears without any action, but check the Stripe dashboard if it persists.
payment_account_inactive
HTTP 422The connected Stripe account is disconnected or disabled.
What to do: Reconnect the account in the dashboard, then publish.
payment_account_onboarding_incomplete
HTTP 422The organizer started Stripe Connect onboarding but never submitted it (details_submitted is false on the connected account).
What to do: Open Payment settings in the dashboard and use Continue Setup, which mints a fresh Stripe Account Link. The outstanding items are collected inside that flow, not in the Stripe dashboard. Two of them (tos_acceptance.date, tos_acceptance.ip) cannot be set there at all.
payment_account_verifying
HTTP 422The organizer submitted Stripe Connect onboarding and Stripe is still reviewing it: details_submitted is true, charges are not enabled yet, and nothing is currently due or past due.
What to do: Wait. Stripe usually finishes within minutes and there is nothing for the organizer to do. Publish again once Payment settings shows the account as connected.
requirements_past_due
HTTP 422Stripe has outstanding requirements past due on the connected account.
What to do: Open the Stripe dashboard and supply the requested information. Publishing stays blocked until Stripe clears the requirement.
Organizer fees
org_fee_pending_approval
HTTP 403The event's organizer-fee configuration is above the auto-approval cap and awaiting review.
What to do: Wait for approval, or lower the fee below the cap, which is the greater of the processing-recovery rate and 8 percent. Changing a ticket price after approval invalidates it and forces re-approval.
org_fee_non_us_blocked
HTTP 403The connected account's country is outside the United States and has not been added to the organization's approved fee regions, while the event has fees configured.
What to do: Contact support to have the country approved, or set the event's fee mode to none. payment_accounts.country is the source of truth here, not the event address; it is synced from the connected Stripe account, so a correction in Stripe reaches it on the next sync.
org_fee_over_max
HTTP 422Creating a ticket type, or changing a ticket price, pushed the organizer fee above the manual review threshold, the lesser of 15 percent and 10 dollars.
What to do: Lower the fee or raise the ticket price so the fee falls under the threshold. Saves above it are blocked outright rather than queued for review.
org_fee_currency_unsupported
HTTP 422The event's currency has no decimal places (JPY, KRW or VND), so the fixed part of the fee ceiling is not modelled for it, and saving the fee configuration (event PATCH, GAP-01 series PUT, SP-03 template PATCH, where it is also the propagate warning code) or creating or repricing a ticket type pushed the organizer fee past that ceiling while it stayed within 15 percent of the ticket price. Returned in place of org_fee_over_max for this case only; the same request is refused either way.
What to do: Organizer fees aren't available in this currency yet. Keep the fee under the ceiling or remove it from the event. Two-decimal currencies never receive this code.
org_fee_free_ticket_conflict
HTTP 422The event has an organizer fee configured and the ticket type you are creating or repricing would leave every ticket type on the event at zero.
What to do: Remove the fee from the event, or keep at least one paid ticket type.
Server
currency_lock_check_failed
HTTP 503The server could not verify whether the event's currency can still be changed.
What to do: Retry the request. Nothing was changed.
internal_error
HTTP 500An unhandled error occurred while processing the request.
What to do: Retry with backoff. If it persists, contact support with the request_id from the response body or the X-Request-Id header, which lets us find the exact request.
insert_failed
HTTP 500The record could not be written.
What to do: Safe to retry with the same Idempotency-Key: nothing was created. Report the request_id if it persists.
query_error
HTTP 500A read query failed while fetching the resource or its related records.
What to do: Retry with backoff. Reads are side-effect free, so retrying is always safe.
update_failed
HTTP 500The record could not be updated.
What to do: Re-read the resource before retrying to confirm whether the write landed, then retry if it did not.
delete_failed
HTTP 500The record could not be deleted.
What to do: Re-read to confirm the current state, then retry. Deletes are idempotent in effect.
Still stuck?
Quote the request_id from the error body, or the X-Request-Id response header. It identifies the exact request in our logs, which is the fastest route to an answer.