# Agency Keys & Cross-Org Targeting

> Mint a single agency-scoped API key to operate any client org across the entire Developer API without one key per client.

Source: https://docs.graph8.com/developers/agency-keys/

An **agency** is an org that manages a portfolio of client orgs linked in graph8 as parent → child. An agency can mint a single **agency-scoped API key** and use it to operate any of its client orgs through the entire Developer API — contacts, sequences, dialer, workflows, appointments, deals, everything — without minting one key per client.

> **Note:**
> Agency key support was added on 2026-06-19. If your org does not have linked client orgs, or is not flagged as an agency, the `is_agency: true` flag returns **400**.

---

## Mint an Agency Key

`POST /api-keys`

Create an agency-scoped API key on the **agency (parent) org** by passing `is_agency: true` in the request body.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | **Yes** | Human-readable label for the key |
| `is_agency` | boolean | No | Set `true` to mint an agency-scoped key. Omit or set `false` for a normal key |

> **Caution:**
> - Requires the standard API-key admin permission on the agency org.
> - Only allowed when the org is a **recognized agency** (it has linked client orgs, or is flagged `is_agency`). Otherwise the API returns **400**.
> - The secret token is shown **once** in the response. Store it securely immediately.

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/api-keys" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CIENCE agency ops",
    "is_agency": true
  }'
```

### Response

```json
{
  "data": {
    "id": "key_abc123",
    "name": "CIENCE agency ops",
    "is_agency": true,
    "token": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}
```

### Errors

| Status | Meaning |
|--------|---------|
| `400` | Org is not a recognized agency (no linked client orgs, not flagged `is_agency`) |
| `403` | Caller lacks API-key admin permission |

---

## Cross-Org Targeting — `X-Target-Org-Id`

Once you have an agency key, send it with the `X-Target-Org-Id` header to act on a specific client org. The request then reads and writes **that client org's** data.

### Targeting Rules

| Request | Result |
|---------|--------|
| Agency key, **no** `X-Target-Org-Id` | **200** — resolves to the **agency's own** org |
| Agency key + `X-Target-Org-Id` for an **authorized** client (case-insensitive) | **200** — operates that client org |
| Agency key + `X-Target-Org-Id` set to the agency's own org ID | **200** — operates the agency org |
| Agency key + `X-Target-Org-Id` for an org **outside** the agency's authorized set | **403** — not authorized for that target |
| **Normal** key + `X-Target-Org-Id` | Header **ignored** — resolves to the key's own org (no error) |

### Notes

- `X-Target-Org-Id` is honored on the **Developer API** (`/api/v1/*`) only. It is not used by in-app or customer-hub hybrid endpoints, which keep single-org semantics.
- `X-Target-Org-Id` is **distinct** from `X-Org-Id`. `X-Org-Id` remains a same-org safety guard; `X-Target-Org-Id` is the agency selector. Do not conflate them. For an agency key, `X-Org-Id` (if sent) still checks against the **agency's own** org, independent of any `X-Target-Org-Id`.
- The target is always **clamped** against the agency's live authorized client set — a caller-supplied value can only select within that set, never widen it.
- **Billing:** credit-consuming actions performed in a client org are charged to **that client org**, not the agency.
- Cross-org **writes** are supported, with one exception: destructive marketplace operations (for example, approving an SDR invoice that fires a Stripe charge) still require a personal admin key and are refused for agency machine keys.

### Example

**cURL**

```bash
# List contacts in a specific client org
curl "https://be.graph8.com/api/v1/contacts" \
  -H "Authorization: Bearer $AGENCY_KEY" \
  -H "X-Target-Org-Id: org_client123"

# Create a contact in a client org
curl -X POST "https://be.graph8.com/api/v1/contacts" \
  -H "Authorization: Bearer $AGENCY_KEY" \
  -H "X-Target-Org-Id: org_client123" \
  -H "Content-Type: application/json" \
  -d '{
    "list_id": 5,
    "first_name": "Jane",
    "last_name": "Smith",
    "work_email": "jane@clientco.com"
  }'
```

---

## Get Agency Credential Info

`GET /agency/me`

Describe the agency credential associated with the current API key. Returns metadata about the agency org and the number of client orgs the key can target.

> **Note:**
> Requires an agency-scoped key. Returns **403** if the key is a normal (non-agency) key.

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/agency/me" \
  -H "Authorization: Bearer $AGENCY_KEY"
```

### Response

```json
{
  "data": {
    "agency_org_id": "org_agency123",
    "agency_org_name": "CIENCE",
    "is_agency": true,
    "client_count": 52,
    "target_header": "X-Target-Org-Id"
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `agency_org_id` | string | The org ID of the agency itself |
| `agency_org_name` | string | Display name of the agency org |
| `is_agency` | boolean | Always `true` for a valid agency key |
| `client_count` | integer | Number of client orgs this key is authorized to target |
| `target_header` | string | The header name to use for cross-org targeting (`X-Target-Org-Id`) |

### Errors

| Status | Meaning |
|--------|---------|
| `403` | Key is not an agency-scoped key |

---

## List Authorized Client Orgs

`GET /agency/clients`

List all client orgs this agency key is authorized to target. Each `org_id` in the response is a valid value for the `X-Target-Org-Id` header.

> **Note:**
> Requires an agency-scoped key. Returns **403** if the key is a normal (non-agency) key.

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/agency/clients" \
  -H "Authorization: Bearer $AGENCY_KEY"
```

### Response

```json
{
  "data": [
    { "org_id": "org_client123" },
    { "org_id": "org_client456" }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `org_id` | string | Client org ID. Use this value as `X-Target-Org-Id` to operate that org |

### Errors

| Status | Meaning |
|--------|---------|
| `403` | Key is not an agency-scoped key |
