Skip to main content
Equaticket

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"
  }
}

Authentication

missing_auth

HTTP 401

No 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 401

The 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 401

The 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 403

The 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 403

A 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 403

The 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 403

The 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 403

The 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 403

On 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 400

On 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 400

On 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 403

A 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 403

Publishing 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 403

A 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 / 422

The 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 400

The 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 400

A 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 400

A 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 400

The 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 400

A 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 400

A 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 422

Venue 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 404

The 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 404

A 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 409

A 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 409

currency 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 409

The 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 409

An 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 409

The 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 409

A 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 409

A 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 409

The 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 400

The 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 422

This 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 409

A 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 429

The 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 422

The 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 422

The 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 422

The 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 422

The 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 422

Stripe 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 422

The connected Stripe account is disconnected or disabled.

What to do: Reconnect the account in the dashboard, then publish.

payment_account_onboarding_incomplete

HTTP 422

The 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 422

The 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 422

Stripe 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 403

The 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 403

The 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 422

Creating 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 422

The 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 422

The 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 503

The 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 500

An 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 500

The 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.

service_unavailable

HTTP 503

A dependency needed to serve the request was temporarily unreachable, most often the credential lookup during authentication.

What to do: Retry with backoff, honouring Retry-After. Do NOT rotate your API key: a 503 says we could not check the key, not that the key is wrong.

query_error

HTTP 500

A 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 500

The 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 500

The 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.