{
  "openapi": "3.1.0",
  "info": {
    "title": "Gotobeat public agent API",
    "version": "1.0.0",
    "summary": "Read gigs and buy tickets on gotobeat.com.",
    "description": "The public surface an autonomous agent needs: read-only event, artist and venue search, plus an Agentic Commerce Protocol checkout. Full prose documentation lives at https://gotobeat.com/docs/agents/. Every path below is explicitly allowed in https://gotobeat.com/robots.txt; every other /api/ path is internal.",
    "contact": {
      "name": "Gotobeat agent documentation",
      "url": "https://gotobeat.com/docs/agents/"
    }
  },
  "servers": [
    {
      "url": "https://gotobeat.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "discovery",
      "description": "Read-only event, artist and venue lookup. No authentication."
    },
    {
      "name": "checkout",
      "description": "Agentic Commerce Protocol checkout sessions."
    }
  ],
  "paths": {
    "/api/events": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "listEvents",
        "summary": "List or search upcoming published events",
        "description": "Without `search`, returns the upcoming published events in chronological order, paginated by an opaque cursor. With `search`, returns direct keyword matches; when there are none the response widens to the matched artists' events, then to genre-matched events, then to the top-selling upcoming events, and reports which of those it did in `fallbackType`. A fallback response is never paginated.",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free-text query. Absent means \"list the upcoming events\".",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor. Pass back the `cursor` value of the previous response. Do not construct one.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Defaults to 12.",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of events. There is no success/data envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/search/event/{keyword}": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "searchEventsByKeyword",
        "summary": "Keyword event lookup",
        "description": "Returns a bare array of sanitised public event objects. There is no envelope. An empty keyword or a failed lookup returns an empty array, never an error status.",
        "parameters": [
          {
            "name": "keyword",
            "in": "path",
            "required": true,
            "description": "The search keyword.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A bare array of sanitised public event objects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PublicEvent"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/artists/search": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "searchArtists",
        "summary": "Artist autocomplete",
        "description": "A keyword shorter than two characters returns an empty `data` array.",
        "parameters": [
          {
            "name": "keyword",
            "in": "query",
            "required": true,
            "description": "The search keyword. Two characters minimum.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching artists, each with an `id` and a display `label`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistSearchResponse"
                }
              }
            }
          },
          "500": {
            "description": "The search failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnvelopeError"
                }
              }
            }
          }
        }
      }
    },
    "/api/venues/search": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "searchVenues",
        "summary": "Venue autocomplete",
        "description": "An empty keyword returns an empty `data` array. Results are de-duplicated by venue id.",
        "parameters": [
          {
            "name": "keyword",
            "in": "query",
            "required": true,
            "description": "The search keyword.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching venues.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VenueSearchResponse"
                }
              }
            }
          },
          "500": {
            "description": "The search failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnvelopeError"
                }
              }
            }
          }
        }
      }
    },
    "/api/checkout_sessions": {
      "post": {
        "tags": [
          "checkout"
        ],
        "operationId": "createCheckoutSession",
        "summary": "Create a checkout session",
        "description": "Each `items[].id` is the `Offer.sku` read from an event page's JSON-LD, in the form `<eventId>:<ticketTypeId>`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {
            "agenticSignature": []
          }
        ]
      }
    },
    "/api/checkout_sessions/{checkoutId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CheckoutId"
        }
      ],
      "get": {
        "tags": [
          "checkout"
        ],
        "operationId": "getCheckoutSession",
        "summary": "Read a checkout session",
        "responses": {
          "200": {
            "description": "The current session state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "agenticSignature": []
          }
        ]
      },
      "post": {
        "tags": [
          "checkout"
        ],
        "operationId": "updateCheckoutSession",
        "summary": "Update a checkout session",
        "description": "Send at least one property. Sending an empty body is a 400.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "agenticSignature": []
          }
        ]
      }
    },
    "/api/checkout_sessions/{checkoutId}/complete": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CheckoutId"
        }
      ],
      "post": {
        "tags": [
          "checkout"
        ],
        "operationId": "completeCheckoutSession",
        "summary": "Pay for a checkout session",
        "description": "Call this once the session reports `ready_for_payment`. The completed session carries an `order` whose `permalink_url` is the buyer-facing confirmation page, `/orders/<checkoutId>`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutCompleteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The completed session, including its order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "agenticSignature": []
          }
        ]
      }
    },
    "/api/checkout_sessions/{checkoutId}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CheckoutId"
        }
      ],
      "post": {
        "tags": [
          "checkout"
        ],
        "operationId": "cancelCheckoutSession",
        "summary": "Cancel a checkout session",
        "description": "Releases the held seats. The request body is ignored.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The cancelled session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "agenticSignature": []
          }
        ]
      }
    }
  },
  "components": {
    "parameters": {
      "CheckoutId": {
        "name": "checkoutId",
        "in": "path",
        "required": true,
        "description": "The id of the checkout session.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A unique key for this request, 255 characters or fewer. A replay of the same key inside the one-hour cache window returns the first response verbatim, with its original status code.",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The payload failed validation, or the Idempotency-Key header is missing or too long.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The HMAC signature is absent, wrong, or its timestamp is more than five minutes from the server clock.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No checkout session has that id.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "PublicEvent": {
        "type": "object",
        "additionalProperties": true,
        "description": "A sanitised public event. Key fields: `eventId`, `slug` (the `/gig/<slug>/` path segment), `title`, `date`, `startTime`, `venue`, `artists` and `tickets`. Each entry of `tickets` carries `ticketTypeId`, `title`, `price`, `currency` and `available`. Join `eventId` and `ticketTypeId` with a colon to get the ACP line-item id."
      },
      "EventListResponse": {
        "type": "object",
        "additionalProperties": true,
        "description": "`events` is the page. `cursor` is the opaque cursor for the next page, or null. `hasMore` says whether a next page exists. `search` echoes the query when one was sent. `fallbackType` (`artist_events`, `artist_no_events` or `top_events`) appears only when the direct search found nothing and the response widened; a fallback response is never paginated. When `fallbackType` is `artist_no_events`, `events` is empty and the suggestions arrive in `matchedArtists` and `genreEvents`.",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicEvent"
            }
          },
          "cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "hasMore": {
            "type": "boolean"
          },
          "search": {
            "type": "string"
          },
          "fallbackType": {
            "type": "string",
            "enum": [
              "artist_events",
              "artist_no_events",
              "top_events"
            ]
          }
        },
        "required": [
          "events",
          "hasMore"
        ]
      },
      "ArtistSearchResponse": {
        "type": "object",
        "additionalProperties": true,
        "description": "Each `data` row carries the artist `id` and a display `label`.",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "VenueSearchResponse": {
        "type": "object",
        "additionalProperties": true,
        "description": "Each `data` row carries `id`, `label`, `venueId`, `venueName`, `city`, `country` and `hiringOptionsCount`.",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EnvelopeError": {
        "type": "object",
        "additionalProperties": true,
        "description": "The enveloped failure shape: `success` is false and `error` holds the message.",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "success",
          "error"
        ]
      },
      "Error": {
        "type": "object",
        "additionalProperties": true,
        "description": "`error` holds the message. `details` may hold the validation issues.",
        "properties": {
          "error": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "error"
        ]
      },
      "Address": {
        "type": "object",
        "additionalProperties": true,
        "description": "A postal address. `line_one`, `city`, `postal_code` and `country` are required; `name`, `line_two`, `state`, `phone`, `company` and `metadata` are optional.",
        "properties": {
          "name": {
            "type": "string"
          },
          "line_one": {
            "type": "string"
          },
          "line_two": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "postal_code": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "minLength": 2
          },
          "phone": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "line_one",
          "city",
          "postal_code",
          "country"
        ]
      },
      "Buyer": {
        "type": "object",
        "additionalProperties": true,
        "description": "Every field is optional, but a session cannot reach `ready_for_payment` without the contact details the tickets are issued to.",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "shipping_address": {
            "$ref": "#/components/schemas/Address"
          },
          "billing_address": {
            "$ref": "#/components/schemas/Address"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "LineItemInput": {
        "type": "object",
        "additionalProperties": true,
        "description": "One ticket tier and how many of it to buy.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The `Offer.sku` from the event page's JSON-LD: `<eventId>:<ticketTypeId>`."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "unit_amount": {
            "type": "integer",
            "minimum": 0,
            "description": "Minor units."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "image_url": {
            "type": "string",
            "format": "uri"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "id",
          "quantity"
        ]
      },
      "FulfillmentOption": {
        "type": "object",
        "additionalProperties": true,
        "description": "`type` is one of `shipping`, `pickup`, `digital` or `local_delivery`. `amount` is in minor units.",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "amount": {
            "type": "integer",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "estimated_days": {
            "type": "integer",
            "minimum": 0
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "id",
          "type",
          "label",
          "amount",
          "currency"
        ]
      },
      "CheckoutCreateRequest": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "buyer": {
            "$ref": "#/components/schemas/Buyer"
          },
          "fulfillment_address": {
            "$ref": "#/components/schemas/Address"
          },
          "fulfillment_option_id": {
            "type": "string"
          },
          "fulfillment_options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FulfillmentOption"
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "currency",
          "items"
        ]
      },
      "CheckoutUpdateRequest": {
        "type": "object",
        "additionalProperties": true,
        "minProperties": 1,
        "description": "Send at least one property. An empty body is a 400.",
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "buyer": {
            "$ref": "#/components/schemas/Buyer"
          },
          "fulfillment_address": {
            "$ref": "#/components/schemas/Address"
          },
          "fulfillment_option_id": {
            "type": "string"
          },
          "fulfillment_options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FulfillmentOption"
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "PaymentData": {
        "type": "object",
        "additionalProperties": true,
        "description": "Send either `token` or `shared_payment_token`.",
        "properties": {
          "provider": {
            "type": "string"
          },
          "token": {
            "type": "string"
          },
          "shared_payment_token": {
            "type": "string"
          },
          "billing_address": {
            "$ref": "#/components/schemas/Address"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "CheckoutCompleteRequest": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "buyer": {
            "$ref": "#/components/schemas/Buyer"
          },
          "payment_data": {
            "$ref": "#/components/schemas/PaymentData"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "payment_data"
        ]
      },
      "CheckoutSession": {
        "type": "object",
        "additionalProperties": true,
        "description": "`status` moves `not_ready_for_payment` to `ready_for_payment` to `completed`, or to `canceled`. `messages` explains what a not-ready session still needs. `line_items` holds the priced tiers, `totals` the amounts in minor units. A completed session carries `order`, whose `permalink_url` is the buyer-facing page `/orders/<checkoutId>`.",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "not_ready_for_payment",
              "ready_for_payment",
              "in_progress",
              "completed",
              "canceled"
            ]
          },
          "currency": {
            "type": "string"
          },
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "totals": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "messages": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "buyer": {
            "$ref": "#/components/schemas/Buyer"
          },
          "fulfillment_address": {
            "$ref": "#/components/schemas/Address"
          },
          "fulfillment_options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FulfillmentOption"
            }
          },
          "order": {
            "type": "object",
            "additionalProperties": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "id",
          "status",
          "currency",
          "line_items"
        ]
      }
    },
    "securitySchemes": {
      "agenticSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "Signature",
        "description": "HMAC-SHA256 of the raw request body with the shared agentic-commerce secret, base64 or hex. Send Signature-Input with a created= timestamp inside a 300-second window. Production enforces this on every /api/checkout_sessions operation; staging does not. The secret comes from the commerce-platform onboarding (OpenAI/Stripe ACP)."
      }
    }
  }
}
