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

# Authentication

> API keys, scopes, IP allowlists, and webhook security.

## API Keys

All requests to the Open API are authenticated with an API key sent via the `api-key` HTTP header.

```bash theme={null}
curl https://api.ticketnation.ph/open-api/v1/me \
  -H "api-key: tn_live_your_api_key_here"
```

### Creating an API Key

<Steps>
  <Step title="Navigate to API Keys">
    Go to **Organizer Dashboard > Settings > API Keys** tab and click **+ Create Key**
  </Step>

  <Step title="Fill in details">
    * **Name** — a label for your integration (e.g., "Experia Integration")
    * **Description** — what this key is used for
  </Step>

  <Step title="Select scopes">
    Check the permissions your integration needs (see table below)
  </Step>

  <Step title="Configure optional settings">
    * **Callback URL** — webhook URL to receive order and event notifications
    * **IP Allowlist** — comma-separated IPs to restrict key usage (leave empty to allow all)
  </Step>

  <Step title="Copy your key">
    The key is displayed once after creation — store it securely
  </Step>
</Steps>

## Scopes

Each API key has specific scopes that control what it can access. Request only the scopes you need.

| Scope             | Dashboard Label     | Allows                                                                                                                                               |
| ----------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `events:read`     | **Read Events**     | List and get events, search venues                                                                                                                   |
| `events:write`    | **Write Events**    | Create, update, publish, unpublish, archive, delete events. Also required for managing **performers**, **schedules**, and **brands** on your events. |
| `tickets:read`    | **Read Tickets**    | List and get ticket types                                                                                                                            |
| `tickets:write`   | **Write Tickets**   | Create, update, publish, mark sold out, delete ticket types                                                                                          |
| `orders:read`     | —                   | List and get orders for your events                                                                                                                  |
| `webhooks:manage` | **Manage Webhooks** | Create, update, delete, and test webhooks                                                                                                            |

<Info>The `orders:read` scope is granted automatically when creating a key with event scopes. It will appear in the dashboard in a future update.</Info>

### Scope Requirements by Resource

| Resource   | Read              | Write             |
| ---------- | ----------------- | ----------------- |
| Events     | `events:read`     | `events:write`    |
| Performers | `events:read`     | `events:write`    |
| Schedules  | `events:read`     | `events:write`    |
| Brands     | `events:read`     | `events:write`    |
| Tickets    | `tickets:read`    | `tickets:write`   |
| Orders     | `orders:read`     | — (read-only)     |
| Venues     | `events:read`     | — (search only)   |
| Webhooks   | `webhooks:manage` | `webhooks:manage` |

If your key lacks a required scope, the API returns `403 FORBIDDEN` with code `SCOPE_REQUIRED`.

## IP Allowlist

For production keys, you can restrict usage to specific IP addresses or CIDR ranges:

* Leave empty to allow all IPs
* Add your server's public IP(s) for security
* Supports individual IPs and CIDR notation

Requests from non-allowlisted IPs receive `403 FORBIDDEN`.

## Webhook Security

When Ticketnation sends webhook events to your server, each request includes an HMAC signature for verification.

### Verifying Webhook Signatures

```typescript theme={null}
import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string,
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}

// In your webhook handler:
app.post('/webhooks/ticketnation', (req, res) => {
  const signature = req.headers['x-tn-signature'] as string;
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET!,
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const { type, data } = req.body;
  console.log(`Received ${type}`, data);

  res.status(200).send('OK');
});
```

### Webhook Events

| Event                  | Trigger                                                  |
| ---------------------- | -------------------------------------------------------- |
| `order.completed`      | A buyer completes a purchase for your event              |
| `order.refunded`       | An order is refunded                                     |
| `event.sold_out`       | All tickets for an event are sold                        |
| `ticket.inventory_low` | A ticket type's remaining quantity drops below threshold |

### Webhook Payload Format

```json theme={null}
{
  "type": "order.completed",
  "data": {
    "orderId": "uuid",
    "orderNumber": "TN-20250615-ABC123",
    "eventId": "uuid",
    "totalAmount": 3000,
    "currency": "PHP",
    "quantity": 2,
    "buyerName": "Juan Dela Cruz",
    "buyerEmail": "juan@example.com"
  },
  "timestamp": "2025-06-15T18:30:00.000Z"
}
```

### Webhook Reliability

* Failed deliveries are retried with exponential backoff (up to 3 attempts)
* Webhooks are auto-disabled after 10 consecutive failures
* Use the test endpoint to verify your webhook URL works
* Check delivery history via the API to debug failures

## Security Best Practices

1. **Rotate keys regularly** — create a new key, update your integration, then delete the old one
2. **Use IP allowlists** in production
3. **Scope minimally** — don't grant `events:write` if you only need `events:read`
4. **Store keys in environment variables**, never in code
5. **Verify webhook signatures** to prevent spoofed events
6. **Set expiration dates** for temporary integrations
