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

# Conventions

> Base URL, identifiers, response envelopes, pagination, and idempotency rules shared by every endpoint

This page documents cross-cutting rules every endpoint follows. Endpoint pages link here instead of repeating the same boilerplate. For status codes, see [Errors](/api-reference/errors).

## Base URL

```
https://api.9pic.ai
```

All endpoints are versioned under `/api/v1/ext/...`.

## Authentication

Every request must send the API key in the `X-API-Key` header:

```
X-API-Key: <your_9pic_api_key>
```

Tokens are created and managed from the Developer Zone in the dashboard. See [Authentication](/api-reference/authentication) for the full workflow and token lifecycle.

## Identifiers

| Identifier   | Type          | Where it comes from                                                                                                                                                                                                                                           |
| ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `org_id`     | number        | Shown in the top-left organisation selector at [admin.9pic.ai](https://admin.9pic.ai). Must match the org the API key belongs to.                                                                                                                             |
| `event_id`   | number        | Shown next to each event in the dashboard event list, or returned by [List Events](/api-reference/list-events).                                                                                                                                               |
| `request_id` | string (UUID) | Returned by selfie-driven endpoints ([Face Search](/api-reference/face-search) — 9Pic FaceFind, [Video Clipping Search](/api-reference/video-clipping-search) — 9Pic Motion). Identifies a single search; reuse it across pagination, retries, and downloads. |
| `image_id`   | string (UUID) | Returned inside `data.images[].image_id` from photo and search endpoints. Identifies a single source photo.                                                                                                                                                   |
| `bib`        | string        | The participant's BIB number, used by **9Pic BibTrack** ([BIB Search](/api-reference/bib-search)). Treated as case-insensitive on the server.                                                                                                                 |

## URL Path Conventions

* **Org-scoped**: `/api/v1/ext/{org_id}/...`
* **Event-scoped**: `/api/v1/ext/{org_id}/event/{event_id}/...`
* **Request-id-scoped**: `.../{request_id}` where `request_id` is a UUID returned by a previous selfie POST.

## Response Envelope

Every endpoint — success or error — returns the same three-key envelope:

```json theme={null}
{
  "responseType": "success",
  "message": "Records for all images",
  "data": {
    "images": [],
    "pagination": { "total": 0, "currentPage": 0, "totalPages": 0, "hasNextPage": false, "hasPreviousPage": false, "page_size": 32 }
  }
}
```

| Field          | Type           | Description                                                                                                  |
| -------------- | -------------- | ------------------------------------------------------------------------------------------------------------ |
| `responseType` | string         | `"success"` on any 2xx response, `"error"` on any 4xx/5xx response.                                          |
| `message`      | string         | Human-readable status or error message. Safe to log; not a stable contract — never branch on its exact text. |
| `data`         | object \| null | The endpoint payload on success, `null` on every error. The payload schema is documented per endpoint.       |

This applies to every `/api/v1/ext/...` endpoint, including [List Events](/api-reference/list-events), [Event Details](/api-reference/event-details), [All Photos](/api-reference/all-photos), [BIB Search](/api-reference/bib-search), [Face Search](/api-reference/face-search), [Video Clipping Search](/api-reference/video-clipping-search), [Download Original Photos](/api-reference/download-original-photos), and [Ping](/api-reference/ping). Endpoint-specific fields like `request_id`, `confidence_percentage`, `status`, `videos`, `pagination`, etc. always live inside `data`.

### Error envelope

Errors use the same shape with `data: null`:

```json theme={null}
{
  "responseType": "error",
  "message": "BIB number 1234 not found for this event",
  "data": null
}
```

The HTTP status code carries the error type; see [Errors](/api-reference/errors) for the full status code table.

## Datetime Format

All `datetime` fields are ISO 8601 strings:

```
2026-01-19T06:00:00
```

Date-only fields are not used. When a datetime field is unset, the value is `null`.

## Pagination

Listing and search endpoints return a `PaginationInfo` object. Defaults and maximums vary per endpoint.

See the canonical model and per-endpoint limits on [Pagination Model](/api-reference/models/pagination).

<Note>
  Some empty-result responses return `currentPage: 0` and `totalPages: 0`. Treat those values as "no page is available" rather than as a normal page number.
</Note>

## `request_id` Idempotency

Selfie-driven searches use a POST-once / GET-many pattern. The `request_id` is the canonical identifier for a single selfie search, not a session token.

| Action                          | Use                                                                                                                                                                                                                                                                       |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| First search                    | `POST /faces` (or `POST /video-clipping/search-by-selfie`) without `request_id`. The server generates one and returns it.                                                                                                                                                 |
| Pagination, refresh, deep-links | `GET /{request_id}` reads cached results and never re-runs face matching.                                                                                                                                                                                                 |
| Retry on POST failure           | Retry with the **same** `request_id`. Successful retries resolve to the cached result.                                                                                                                                                                                    |
| Cross-feature reuse             | Reuse the Face Search `request_id` when starting [Video Clipping Search](/api-reference/video-clipping-search), and as `identifier` (with `method: "selfie"`) on [Download Original Photos](/api-reference/download-original-photos). One selfie upload powers all three. |

<Warning>
  A new `POST` with a fresh `request_id` (or no `request_id`) is treated as a brand-new billable search. Avoid issuing fresh POSTs on every page change, refresh, or component remount.
</Warning>

<Tip>
  Persist `request_id` in URL state (for example, `/results/:request_id`) so refreshes and deep-links survive without re-uploading the selfie.
</Tip>

See [Face Search](/api-reference/face-search) and [Video Clipping Search](/api-reference/video-clipping-search) for endpoint-specific behaviour.

## Async Endpoints

[Video Clipping Search](/api-reference/video-clipping-search) is asynchronous: `POST` returns immediately with `data.status: "processing"` and `data.request_id`. Poll `GET /video-clipping/{request_id}` until `data.status` is anything other than `processing`. Recommended interval: exponential backoff starting at 3s.

## Host Validation

Requests are validated against an approved hostname allowlist. Calls from non-approved hosts return `403`. The production allowlist includes `api.9pic.ai`. Talk to support if you need an additional host enabled for testing.

## Error Codes

A short error table is included on every endpoint page. The full list of status codes, common causes, and recommended client behaviour lives on the [Errors](/api-reference/errors) page.
