> ## Documentation Index
> Fetch the complete documentation index at: https://docs.platnova.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Experiences

> Sell tickets to the events and experiences Platnova lists, paid from your business wallet, and deliver them with a single-use ticket link.

You list the experiences Platnova sells, buy tickets for your guest, and send them a single-use link that shows the tickets. Each order is paid from your NGN business wallet. If tickets can't be issued, or the organiser cancels the experience, the money goes back to your wallet automatically.

Experiences must be enabled for your business. Until they are, every route answers `403` `partner_product_disabled`. If the service is unavailable, routes answer `503` `experiences_unavailable`.

All amounts are integers in kobo. A `partner_reference` here is **1 to 64 letters, digits, `.`, `_` or `-`, and never `..`**; anything else gets `422` `partner_reference_invalid`.

## How it works

1. **List experiences** with `GET /v1/partner/experiences`. Each tier has a price per ticket, in NGN, including our fee.
2. **Buy** with `POST /v1/partner/experiences/orders`. Your wallet is debited at once and the answer is `202`.
3. **Deliver.** When the tickets are issued you receive `experience.order.ready` with a ticket link. Send that link to your guest by email, SMS or your own screen. The guest shows the ticket's QR code or code at the door.

## List experiences

`GET /v1/partner/experiences` lists experiences on sale, soonest first. `GET /v1/partner/experiences/{experience_id}` reads one.

**A ticket's price covers what it costs Platnova**: the tier's price at our exchange cost, plus a minimum margin. An order costs `unit_amount_minor × quantity`. Always buy at that amount; a different amount is refused `price_changed`.

```json theme={null}
[
  {
    "experience_id": "8b1c9a40-2f1e-4d1a-9c3b-0a1b2c3d4e5f",
    "name": "Lagos Jazz Night",
    "description": "An evening of live jazz.",
    "location": "Eko Hotel, Victoria Island",
    "category": "music",
    "starts_at": "2026-11-14T19:00:00Z",
    "ends_at": "2026-11-14T23:00:00Z",
    "banner": ["https://storage.googleapis.com/…/banner.jpg"],
    "tiers": [
      {
        "tier_id": "5d2e7f10-8a4b-4c6d-9e1f-2a3b4c5d6e7f",
        "name": "Regular",
        "unit_amount_minor": 550000,
        "currency": "NGN",
        "available": 120,
        "max_per_order": 10
      }
    ]
  }
]
```

## Buy tickets

`POST /v1/partner/experiences/orders` (needs an `Idempotency-Key`)

```json theme={null}
{
  "partner_reference": "SHL-EXP-1",
  "experience_id": "8b1c9a40-2f1e-4d1a-9c3b-0a1b2c3d4e5f",
  "tier_id": "5d2e7f10-8a4b-4c6d-9e1f-2a3b4c5d6e7f",
  "quantity": 2,
  "amount_minor": 1100000,
  "currency": "NGN",
  "guest": { "name": "Ada Obi", "email": "ada@example.com", "phone": "+2348000000000" }
}
```

* One tier per order, 1 to 10 tickets. The tickets are issued in the guest's name; the organiser sees that name and email on the attendee list.
* `phone` is optional. Promo codes are not accepted (`422` `promo_not_supported`).
* Sending the same request again with the same `partner_reference` returns the same order. The same reference with anything different, the guest included, is `409` `partner_reference_reused`.

The answer:

| HTTP | `data.order.status` | Meaning |
| - | - | - |
| `202` | `processing` or `ready` | Being issued, usually within a second. Wait for `experience.order.ready`. |
| `202` | `submitting` | We debited your wallet but did not hear back in time. We check within 30 minutes. If no tickets were issued, the money comes back. |
| `409` `price_changed` | `failed` | The price moved and nothing was bought. `data.price` has `unit_amount_minor` and the new `amount_minor`; your wallet is refunded. Order again with a new reference. |
| `409` `sold_out` | `failed` | Not enough tickets are left. `data.available` says how many; refunded. |
| `409` `experience_unavailable` | `failed` | The experience or tier is no longer on sale; refunded. |
| `202` | `attention` | We could not confirm the outcome. Your money is held while we check it, and we contact you. |

If the last tickets sell out between our check and the issue, you receive `experience.order.failed` with the refund instead of `ready`.

These are refused before any money moves: `422` `insufficient_balance`, `422` `wallet_not_found`, `400` `experience_order_invalid`, `422` `experience_id_invalid`, and your limits (`partner_per_transaction_cap`, `partner_daily_cap`).

## The ticket link

The `experience.order.ready` webhook carries:

```json theme={null}
"ticket_link": { "ticket_url": "https://engine.platnova.com/v1/experience/tickets/<token>", "expires_at": "2026-10-25T10:00:00Z", "single_use": true }
```

* **Give the link only to your guest.** It is the tickets. Do not log it.
* Opening the link is safe: link previews in WhatsApp or Slack don't use it up. It is used up when your guest taps **Show my tickets**. The page then shows each ticket's QR code and code once; the guest should screenshot them.
* **It works once, for 72 hours.** For a new one, call `POST /v1/partner/experiences/orders/{partner_reference}/ticket-link`. Each new link stops the earlier unused ones from working.

Neither the API nor any webhook ever carries a ticket code.

## Cancel

`POST /v1/partner/experiences/orders/{partner_reference}/cancel` (needs an `Idempotency-Key`)

Experiences carry no refund rule of their own, so **an order is refunded only when the organiser cancelled the experience**, and only if none of its tickets was used at the door. When the organiser cancels, we cancel and refund every order for it ourselves within minutes, and send `experience.order.cancelled`; this route lets you do it at once.

| HTTP | `data.code` | Meaning |
| - | - | - |
| `200` | | Cancelled. The tickets no longer work and your wallet is refunded. |
| `409` | `not_refundable` | The experience is going ahead, or a ticket was already used. |
| `409` | `not_cancellable` | The tickets were never issued (see the order's status). |

## Read an order

`GET /v1/partner/experiences/orders/{partner_reference}` returns `{partner_reference, experience_id, tier_id, quantity, status, order_id, amount_minor, currency, transaction_ref, refund_minor}`.

`status` is `submitting`, `processing`, `ready`, `failed` (refunded), `cancelled` (refunded) or `attention` (held while we check).

## Webhooks

Subscribe to `experience.order.*`:

| Event | When | Money |
| - | - | - |
| `experience.order.ready` | The tickets are issued. Carries `ticket_link`. | — |
| `experience.order.failed` | No tickets were issued. | `refund` is already credited to your wallet. |
| `experience.order.cancelled` | The organiser cancelled; the tickets no longer work. | `refund` is already credited to your wallet. |

Each order is refunded at most once, however many times an event is delivered. **An order in `attention` is never refunded automatically.** A person checks it and contacts you.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.