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.
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
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.
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.
org_fee_over_max
HTTP 422A ticket price change 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_free_ticket_conflict
HTTP 422A fee is configured on a ticket type priced at zero.
What to do: Remove the fee from free ticket types, or give the ticket type a non-zero price.
Server
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.