---
title: Identifiers
description: Stripe-style IDs with a region segment. Format, prefixes, who generates them.
section: concepts
order: 8
---

# Identifiers

Every resource has a public identifier with a stable format. Appears in API requests, responses, dashboard URLs, log lines, webhook payloads, support tickets.

## Format

```
<prefix>_<region>_<32 hex>
```

- **prefix**: 3-7 lowercase letters identifying the resource type
- **region**: `eu` or `us`
- **random**: 32 hex characters from a UUIDv7 with dashes stripped

```
run_eu_018f3a2b9c1d7e8fa4b9c2d7e8f1a3b6
```

The first 12 hex characters encode the UUIDv7 millisecond timestamp. IDs sort chronologically as strings; that's what keeps the `runs` and `events` tables fast at scale.

## Prefix table

| Resource | Prefix |
|----------|--------|
| Account | `acct_` |
| User | `user_` |
| API key | `apk_` |
| Ping token | `ping_` |
| Agent | `agt_` |
| Run | `run_` |
| Event | `evt_` |
| Score | `scr_` |
| Check | `chk_` |
| Check result | `chkr_` |
| Rubric | `rub_` |
| Rubric version | `rubv_` |
| Alert route | `aroute_` |
| Alert rule | `arule_` |
| Webhook delivery | `whd_` |
| Rate card entry | `rce_` |

Teams are absent. A team is addressed by its URL slug (`/teams/{slug}`), not an external ID.

## Region segment

Mandatory on every identifier. Sourced from `accounts.data_residency`, captured at signup. Three purposes:

1. **Operator clarity.** `apk_us_018f...` makes data jurisdiction obvious without a database lookup.
2. **Edge region gate.** The edge parses the region from the credential and rejects cross-region requests before touching Postgres.
3. **Routing.** Region-specific ingest endpoints can pair with region-tagged credentials.

Allowlist is `eu` and `us`. Anything else returns `400 Bad Request` with the unknown region named.

## Who generates the ID

| Resource type | Generator | Why |
|---------------|-----------|-----|
| Runs, events, scores | Client (SDK) | High volume, non-blocking dispatch, retries are idempotent |
| Accounts, users, API keys, alert routes, rubrics | Server | Configuration resources from the dashboard |

The SDK constructs the run ID locally before any network call. That's what lets a finish call dispatch before run start is acknowledged.

## Uniqueness and idempotency

IDs are globally unique, enforced by `UNIQUE` constraints on every external_id column. Since IDs are client-generated and SDKs retry on network failure, every write endpoint is idempotent on the ID:

- `POST /v1/runs` with an existing ID returns the record if `agent` and `started_at` match, `409 Conflict` otherwise.
- `POST /v1/runs/{id}/events` silently drops duplicate event IDs.
- `POST /v1/runs/{id}/finish` on a terminal run returns the existing finish idempotently.

Safe retries are free.

:::note
Test mode. AgentPing doesn't currently split IDs into `_live_` and `_test_` variants. For isolated test traffic, create a separate team. The format reserves space for a future `_test_` segment without breaking existing IDs.
:::
