# Organizer API

Get event, order and ticket data and check tickets in from your own app with an API key.

> Source: https://help.nyebar.id/en/organizer/integrations/organizer-api

With the organizer API, your own app or system can read your events, ticket types, orders, tickets and stats, and check tickets in. For example, to copy your attendee list into Google Sheets, show tickets sold on a screen, or use your own scanner app at the gate.

If your system needs to know as soon as there's a new order or check-in, use [Webhooks](/en/organizer/integrations/webhooks) instead of asking the API over and over. API keys are created by the owner or an Admin in the **Integrations** menu. The second half of this article is for the developer who sets up the API.

## Creating an API key

1. Open **Integrations** in the sidebar and click **Create API key**.

   ![The Integrations page with the list of API keys and the Create API key button.](https://help.nyebar.id/screenshots/en/org-integrations.webp "Your API keys on the Integrations page.")

2. Give it a **Key name** that's easy to recognize, such as the name of the app that uses it.
3. Pick the **Access**: **Read data** to see events, orders, tickets and stats, and **Check-in** to check tickets in. Only pick what's needed.
4. Click **Create key**.

   ![The Create API key window with the key name and access choices.](https://help.nyebar.id/screenshots/en/org-api-key-create.webp "Name the key and pick its access.")

5. Click the copy button to copy the key and keep it somewhere safe. The key is only shown this once. If you lose it, revoke the key and create a new one.

   ![The new API key, shown once, with a copy button.](https://help.nyebar.id/screenshots/en/org-api-key-created.webp "The key is only shown once, so copy it right away.")

An organizer can have up to 5 active keys. Keys belong to the organizer, so they keep working when the person who created them leaves the team.

> **Keep keys secret**
>
> Anyone holding a key can read your attendees' data. Keep keys only on a server or in internal apps, never in website code that visitors can see. If a key leaks, revoke it right away.

## Revoking a key

On the **Integrations** page, click the trash icon on the key's row and confirm with **Revoke key**. Apps using that key lose access to the API right away. The **Last used** column helps you find keys nobody uses anymore.

## For developers

### Address and authentication

Send every request to `https://api.nyebar.id/api/org/v1`, with the key in the `Authorization` header:

```bash
curl https://api.nyebar.id/api/org/v1/me \
  -H "Authorization: Bearer nyb_live_..."
```

```json
{
  "organizer": { "id": 19, "name": "Komunitas Lari Nusantara", "slug": "lari-nusantara" },
  "key": { "id": 1, "name": "Gate scanner app", "scopes": ["read", "checkin"] }
}
```

Times are in RFC 3339 format in UTC, and all amounts are whole rupiah.

### Endpoints

| Method | Path | Access | Returns |
| --- | --- | --- | --- |
| GET | `/me` | any | The organizer and the key in use |
| GET | `/events` | Read data | Events. Filters: `status` (`draft`, `review`, `published`, `rejected`), `ended` (`true`/`false`) |
| GET | `/events/{id}` | Read data | An event with its ticket types |
| GET | `/events/{id}/stats` | Read data | Tickets sold, check-ins, gross revenue, add-ons and views |
| GET | `/events/{id}/orders` | Read data | Orders. Filter: `status` (`pending`, `confirmed`, `cancelled`, `awaiting_approval`, `rejected`) |
| GET | `/events/{id}/tickets` | Read data | Issued tickets. Filter: `checked_in` (`true`/`false`) |
| POST | `/events/{id}/check-ins` | Check-in | Check a ticket in by its code |

Another organizer's event is answered like a missing one (`404`).

### Check-in

```bash
curl -X POST https://api.nyebar.id/api/org/v1/events/134/check-ins \
  -H "Authorization: Bearer nyb_live_..." \
  -H "Content-Type: application/json" \
  -d '{"code": "NYED3NKXDBK"}'
```

```json
{
  "status": "checked_in",
  "ticket": {
    "id": 512,
    "code": "NYED3NKXDBK",
    "holder_name": "Rina Putri",
    "holder_email": "rina@example.com",
    "ticket_type": { "id": 235, "name": "Reguler" },
    "bib_number": null,
    "checked_in_at": "2026-10-11T14:11:05Z",
    "created_at": "2026-10-02T03:20:41Z"
  }
}
```

If the ticket was already checked in, `status` is `already_checked_in` and `checked_in_at` shows the first check-in. So it's safe to send the same request again.

### Next pages

List endpoints take `limit` (1 to 100, default 20) and `cursor`. They answer:

```json
{ "data": [ ... ], "next_cursor": "eyJpZCI6...", "has_more": true }
```

While `has_more` is `true`, send the same request again with `cursor` set to `next_cursor`.

### Errors

Errors come with an HTTP status and a body like this. Use `code` in your app, not `message`:

```json
{ "message": "event not found", "code": "event_not_found", "request_id": "99e8fa6b-..." }
```

| Status | `code` | Meaning |
| --- | --- | --- |
| 400 | `invalid_parameter`, `invalid_cursor`, `validation_failed` | A parameter or the request body isn't valid |
| 401 | `invalid_api_key` | The key is missing, wrong or revoked |
| 403 | `api_key_scope_missing` | The key doesn't have access to this endpoint |
| 404 | `event_not_found`, `ticket_not_found` | No such event or ticket in your organizer |
| 409 | `order_not_confirmed`, `ticket_not_issued` | The ticket can't be checked in yet |
| 429 | `rate_limited` | Too many requests |

### Rate limit

Each key can make 120 requests per minute. Every response has the `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix time when the allowance resets) headers. Past the limit, wait for the time in the `Retry-After` header.
