Developers
Sign in Get API keys

API reference · v1

Sell Ayatickets events in your own product

A REST API for events, ticket inventory, checkout sessions and order status, with signed webhooks. Build against sandbox first. Test keys never move real money or reserve real tickets.

Base URL developers.ayatickets.com/api/v1
Auth header X-API-Key: ayt_test_...
Rate limits per minute 120 live · 240 test · 30 checkouts

Quickstart

1 Create an app and a key

Start with a sandbox key.

2 Fetch events and tickets

Events, then dates, then ticket types.

3 Create a checkout session

Send the customer to checkout_url.

Endpoints

All paths are relative to the base URL.

Authentication

Send your secret key in the X-API-Key header on every request. Keys belong to one app. Keep them on your server, never in a browser or mobile app.

Test and live mode

  • ayt_test_* keys hit the sandbox. Checkout is simulated, order references start with sbx_ and no money moves.
  • ayt_live_* keys create real orders and hosted payments.
  • A key can only read orders from its own environment, otherwise you get environment_mismatch.

Rate limits

  • 60 failed-auth attempts a minute per client IP (missing, invalid, revoked, or suspended keys). Valid keys are never counted against this bucket.
  • 120 requests a minute per live app, 240 per test app (after a valid key).
  • 30 checkout session creates a minute per app.
  • Over the limit you get HTTP 429 with rate_limit_exceeded. Back off using Retry-After.

Catalog visibility

Events marked hidden_from_listings in Ayatickets are intentionally excluded from every catalog and checkout endpoint. Partners cannot sell unlisted events through the API.

Authenticated request
curl 'https://developers.ayatickets.com/api/v1/events' \
  -H 'X-API-Key: ayt_test_...'

GET /events

List events

Returns published events (<code>published: true</code>), newest first, with a pricing summary. Response <code>meta</code> includes <code>include_past</code> (always false on this route), pagination fields, and <code>timezone</code>. Checkout responses also echo the HTTP status in <code>meta.status_code</code>.

page integer Optional
Page number. Defaults to 1.
per_page integer Optional
Results per page. Defaults to 20, max 100.
q string Optional
Search event names.
include_past boolean Optional
When true, include events whose last date has ended. Defaults to false.
Request
curl 'https://developers.ayatickets.com/api/v1/events?per_page=2' \
  -H 'X-API-Key: ayt_test_...'
Response · 200 OK
{
    "data": [
        {
            "id": 1933,
            "reference": "dc107c7242",
            "name": "Bombele Night",
            "slug": "bombele-night",
            "active_dates_count": 1,
            "next_startdate_iso": "2026-12-19T19:30:00+00:00",
            "pricing": {
                "has_free_tickets": false,
                "has_paid_tickets": true,
                "is_entirely_free": false
            }
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 2,
        "total": 38,
        "has_next": true,
        "timezone": "Africa/Accra"
    }
}

GET /events/{id}

Get an event

Returns one published event. Unpublished or missing events return 404 event_not_found.

id integer Required
The event ID from List events.
Request
curl 'https://developers.ayatickets.com/api/v1/events/1933' \
  -H 'X-API-Key: ayt_test_...'
Response · 200 OK
{
    "data": {
        "id": 1933,
        "name": "Bombele Night",
        "description": "Tickets on Ayatickets.",
        "image_url": "https://cdn.ayatickets.com/uploads/events/65aae9be417d627270cb306f50d5f855.jpg",
        "has_multiple_dates": false,
        "timezone": "Africa/Accra"
    }
}

GET /events/{id}/dates

List event dates

Returns upcoming active dates for an event, with venue details. Pick one to fetch its tickets.

id integer Required
The event ID.
include_past boolean Optional
Include dates that have already ended. Defaults to false.
Request
curl 'https://developers.ayatickets.com/api/v1/events/1933/dates' \
  -H 'X-API-Key: ayt_test_...'
Response · 200 OK
{
    "data": [
        {
            "id": 5085,
            "event_id": 1933,
            "startdate_iso": "2026-12-19T19:30:00+00:00",
            "online": false,
            "venue_name": "ROYAL MARICAY",
            "venue_city": "Tanoso"
        }
    ],
    "meta": {
        "event_id": 1933,
        "count": 1,
        "include_past": false
    }
}

GET /event-dates/{id}/tickets

List tickets

Ticket types for one event date, with live price and quantity_available. The organizer <code>free</code> flag is a boolean; use <code>is_free</code>, <code>effective_price</code>, and <code>requires_payment</code> for checkout pricing (matches Ayatickets core <code>getSalePrice()</code>).

id integer Required
The event date ID.
Request
curl 'https://developers.ayatickets.com/api/v1/event-dates/5085/tickets' \
  -H 'X-API-Key: ayt_test_...'
Response · 200 OK
{
    "data": [
        {
            "id": 508065,
            "name": "Girls",
            "currency": "GHS",
            "effective_price": "25.00",
            "quantity_available": 212,
            "requires_payment": true
        }
    ],
    "meta": {
        "event_id": 1933,
        "event_date_id": 5085
    }
}

POST /checkout-sessions

Create checkout session

Creates a hosted checkout for one event date. We work out prices and check stock on our side, so never send a price or total. Live free carts complete immediately with status: completed, requires_payment: false, and checkout_url: null (webhook order.completed). customer.phone must be a valid Ghana mobile number for live free checkout. Paid live carts return status: pending with a hosted checkout_url.

event_id integer Required
The published event.
eventdate_id integer Required
Which date of the event.
items array Required
Up to 20 items of ticket_id and quantity.
customer object Required
Name, email and phone. Used only to fulfil the order.
success_url, cancel_url string Required
HTTPS, on a domain in your allowlist.
metadata object Optional
Your own references. Max 20 keys or 4 KB. No personal data.

Idempotency-Key is required. Same key and same body returns the saved response. Same key with a different body returns 409 idempotency_conflict. Keys are kept for 60 minutes.

Redirect allowlist. success_url and cancel_url must be HTTPS and their host must be on your app's allowed redirect domains. Add domains on the app page in the developer portal.

Request
curl -X POST 'https://developers.ayatickets.com/api/v1/checkout-sessions' \
  -H 'X-API-Key: ayt_test_...' \
  -H 'Idempotency-Key: checkout-8f2b' \
  -H 'Content-Type: application/json' \
  -d '{
    "event_id": 1933,
    "eventdate_id": 5085,
    "items": [{ "ticket_id": 508065, "quantity": 2 }],
    "customer": { "name": "Kwame Mensah", "email": "kwame@company.com", "phone": "233200000000" },
    "success_url": "https://merchant.com/success",
    "cancel_url": "https://merchant.com/cancel"
  }'
Response · 201 Created
{
    "order_reference": "ORD-2026-00124",
    "checkout_url": "https://checkout.ayatickets.com/session/...",
    "expires_at": "2026-03-15T12:05:00+00:00",
    "status": "pending",
    "environment": "live",
    "requires_payment": true,
    "total_amount": "50.00"
}

In sandbox, checkout simulates success, failure and cancel. Confirm payment with a webhook or GET /orders/{reference}, never from the redirect alone.

GET /orders/{reference}

Get order status

Status for an order your app created. Use it with webhooks as the source of truth before you fulfil an order. <code>data.status</code> is <code>pending</code>, <code>paid</code>, <code>completed</code> (live developer free orders), <code>failed</code>, <code>expired</code>, or <code>unknown</code>. Sandbox free checkouts may still report <code>paid</code>.

reference string Required
The order_reference from Create checkout session. Sandbox references start with sbx_.
Request
curl 'https://developers.ayatickets.com/api/v1/orders/ORD-2026-00124' \
  -H 'X-API-Key: ayt_test_...'
Response · 200 OK
{
  "data": {
    "reference": "ORD-2026-00124",
    "status": "paid",
    "currency": "GHS",
    "payment_reference": "AYT-PAY-88213",
    "environment": "live"
  }
}

Webhooks

Treat webhooks and order status as the source of truth for fulfilment, not the redirect.

  • Events: order.paid order.completed (live free checkout) order.failed order.expired webhook.test
  • Headers: X-AYT-Webhook-Timestamp, X-AYT-Webhook-Signature, X-AYT-Event-Id, X-AYT-Event-Type
  • Retries with exponential backoff, up to 10 attempts, on any non-2xx response.
  • Use X-AYT-Event-Id to ignore duplicates.
Verify a signature · Node.js
const signed = timestamp + "." + rawBody;
const expected = crypto
  .createHmac("sha256", webhookSecret)
  .update(signed)
  .digest("base64");
// Compare in constant time.
// Reject if the timestamp is over 5 minutes old.

The signature is base64(HMAC-SHA256(webhook_secret, timestamp + "." + raw_body)). Use the raw request body, before any JSON parsing.

Errors

Every error has the same shape: an error object with code, message and, for validation, details.

HTTPCodeWhen it happens
401 missing_api_key GET/POST without X-API-Key (counts toward failed-auth IP bucket).
400 missing_idempotency_key Checkout request sent without Idempotency-Key.
400 invalid_idempotency_key Idempotency-Key header is too long.
401 invalid_api_key API key missing, malformed or revoked.
401 unauthorized Authenticated app context missing on an internal route.
403 app_suspended The app is suspended.
403 account_disabled The developer account is disabled.
403 account_pending_approval Your account is waiting for approval.
403 environment_mismatch Key environment does not match the order (including GET /orders).
403 event_not_available Event is private, password-protected, or otherwise unavailable for checkout.
404 event_not_found Event does not exist, is not published, or is hidden from listings.
404 event_date_not_found Event date does not exist or is not active.
404 not_found Unknown API route.
404 order_not_found Order is not linked to your app.
405 method_not_allowed HTTP method is not allowed for this endpoint.
409 idempotency_conflict Same key used with a different payload.
409 idempotency_in_progress The first request with this key is still running.
409 sold_out Ticket inventory is exhausted.
422 validation_failed Payload failed validation (fields, types, URLs, max 20 items).
422 event_date_not_available Event date is inactive, ended, or payout-locked.
422 ticket_not_found Ticket is invalid for the event date.
422 ticket_not_on_sale Ticket or event sales window has not started or has ended.
422 ticket_quantity_exceeded Quantity exceeds tickets-per-attendee limit.
422 invalid_redirect_url success_url or cancel_url is not a valid HTTPS URL.
422 redirect_allowlist_required App has no allowed redirect domains.
422 redirect_domain_not_allowed success_url or cancel_url host is not allowed.
422 metadata_too_large Metadata over 20 keys or 4 KB.
429 rate_limit_exceeded Too many failed-auth attempts per IP or too many requests per app. Retry-After header is set.
422 quantity_exceeds_limit First live free order for this buyer/event exceeds the per-buyer daily ticket cap (UTC day boundaries).
429 free_order_limit_reached Daily live free ticket cap reached for this app or buyer/event (UTC day boundaries).
503 checkout_unavailable Checkout is temporarily unavailable. Retry, then contact support.
503 free_checkout_unavailable Live free checkout kill switch is off.
503 payment_gateway_unavailable Free payment gateway unavailable (live free checkout).
500 internal_error Unexpected server error (non-debug environments).
Error shape
{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed.",
    "details": { "items.0.quantity": ["The quantity must be at least 1."] }
  }
}

Security and data

Keys stay server-side

We store only a hash of each key. Revoke a leaked key from the app page and create a new one.

Payments stay with us

Buyers enter payment details on hosted checkout. They never touch your servers.

Minimal personal data

Customer name, email and phone are used only to fulfil tickets. Keep personal data out of metadata. Retention or deletion requests: developers@ayatickets.com.