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

# Platform tenancy

> The provider-tenant record that anchors your rate limits, safety margins, and dispatch lanes

Every platform you connect to has its own notion of "your account over there." Five9 calls it a **Domain**. Your rate limits, your quota budget, and your per-action dispatch settings all belong to that record — not to the individual credentials you used to reach it. GetDialed stores it as a first-class object so N sets of credentials pointing at the same Domain share one budget instead of racing each other into a throttle.

## What a Domain is

A Domain is Five9's own tenant boundary. You never type its identifier in: when you create a Five9 [connection](/concepts/connections), GetDialed calls Five9 with the credentials you supplied, reads back the canonical Domain identifier from `getVCCConfiguration`, and binds the connection to that Domain — creating the record if this is the first time your account has connected to it.

The label comes from the [catalog](/concepts/catalog). Each platform publishes a `tenancy_unit` describing what it calls its own tenant record, so the API can label it correctly without a hard-coded lookup:

```json theme={null}
{
  "tenancy_unit": {
    "name": "Domain",
    "plural": "Domains",
    "key_field": "domain_id",
    "key_source": "getVCCConfiguration.domainId"
  }
}
```

Five9 returns "Domain". A future platform will return its own word — Salesforce would say "Instance" — and the same concepts on this page apply under that name. Platforms with no tenant boundary of their own return `null` and have no tenancy record at all.

## One Domain, one budget

Five9's rate limits are counted **per Domain**, not per login. Two connections using two different Five9 users against the same Domain draw down the same 20-calls-per-minute upload allowance. GetDialed models that directly: the second connection resolves to a Domain that already exists, so it binds to the existing record rather than creating a duplicate.

Every connection carries the resulting `tenancy_id` back-reference:

```json theme={null}
{
  "id": "conn_e5f6g7h8",
  "name": "Five9 Production",
  "platform_id": "five9",
  "service_id": "five9__configuration_service",
  "auth_method": "basic_auth",
  "status": "active",
  "tenancy_id": "ten_9f2c1a7b3d4e5f60"
}
```

`tenancy_id` is server-managed — you cannot set it on create or update, and any value you send is ignored. Connections sharing a `tenancy_id` are credential lanes into one budget.

## One account per Domain

A Five9 Domain belongs to exactly **one** GetDialed account, platform-wide. This is not a per-organization check: if any other GetDialed account has already connected that Domain, your create is refused.

* `POST /connections` returns `409 Conflict` with a message telling you the Domain is already connected to another account and to reach out to us for support. Nothing is stored — no credentials, no record.
* Both credential-test endpoints report the same thing at `200` with `error: "PERMISSION_DENIED"`, so you find out before you save. Testing writes nothing either way.
* `PUT /connections/{connection_id}` runs the same check. If you change a connection's credentials and they resolve to a *different* Domain, the connection re-binds itself automatically — unless that Domain belongs to someone else, in which case the update is refused and your existing binding is left working and untouched.

<Note>
  Resolution is a network call to Five9. If the credentials are wrong or Five9 cannot be reached, creating the connection fails with a `400` rather than storing a connection that has no Domain. The failure message tells you which of the two happened without echoing anything Five9 said back.
</Note>

Deleting the **last** connection bound to a Domain releases the platform-wide claim, and any account — including yours — is free to claim it again. There is no support gate and no waiting period. The Domain record itself is archived rather than destroyed, so re-adding a connection restores the safety margins and lane settings you configured.

## Rate buckets and effective limits

A Domain groups its limits into **rate buckets**, keyed by Five9's own `apiOperationType` names (`Query`, `Modify`, `Upload`, and so on). Each bucket holds the raw provider caps for three windows — 60 seconds, 3,600 seconds (one hour), and 86,400 seconds (one day) — seeded from the catalog defaults and then corrected against a live `getCallCountersState` reading taken on the same session that resolved the Domain.

On top of the raw caps sits a **safety margin**: a percentage, set per bucket, that can only ever *reduce* the budget.

```
effective = cap × margin_pct / 100
```

Raw caps and margin are stored separately and the product is computed at read. That is what lets a limits refresh overwrite the provider caps without clobbering your margin, and a margin edit change nothing about the provider caps. Margins accept `1`–`100`; anything above 100 is rejected, because a margin above the provider cap would hand out budget Five9 never granted, and `0` would be a permanent outage rather than a safety margin.

```json theme={null}
{
  "Upload": {
    "label": "Uploading (batch)",
    "caps": { "60": 20, "3600": 400, "86400": 2000 },
    "margin_pct": 80,
    "max_records_per_request": 50000,
    "source": "live",
    "observed_at": "2026-07-25T18:00:00Z",
    "effective": { "60": 16, "3600": 320, "86400": 1600 }
  }
}
```

The record's `sync_status` tells you where the caps came from:

| Value             | Meaning                                                                                              |
| ----------------- | ---------------------------------------------------------------------------------------------------- |
| `catalog_default` | The caps are the provider's published defaults from the catalog. No live reading has landed yet.     |
| `live`            | The caps were observed from Five9 and folded into the record. `counters_synced_at` records when.     |
| `stale`           | A refresh was attempted but the reading came back unusable, so the previous caps are still in force. |

`stale` is a deliberate outcome, not a failure to fix. A garbled or zero-valued counter reading never overwrites a good stored cap, because a bad reading that zeroed a budget would silently stop all delivery against that Domain. Inspect `sync_status` rather than the status code when you refresh.

## Lanes and ordering

Actions against a Domain are placed into one of four named dispatch lanes:

| Lane          | Intended for                                                                                |
| ------------- | ------------------------------------------------------------------------------------------- |
| `compliance`  | Regulatory work that must not wait behind bulk traffic — DNC writes are the canonical case. |
| `interactive` | Small, latency-sensitive calls a person is waiting on.                                      |
| `bulk`        | High-volume writes, such as batched list uploads. The default lane.                         |
| `backfill`    | Lowest-priority catch-up work that should yield to everything else.                         |

The catalog seeds a default lane per action. An admin can override that placement for their own Domain (`lane_overrides`) and fix the order actions run in within a lane (`lane_order`). Ordering is an explicit ordered list of action IDs, not a numeric weight — the lane set is fixed and finite so the resulting order is deterministic and explainable.

<Warning>
  Lane configuration is **stored** today, not yet acted on. The dispatcher that consumes lanes and paces work against the effective budget ships in a later release; until then, setting a lane records your intent and changes nothing about how work is delivered.
</Warning>

## Endpoints

Domains are system-managed: they are created by connection binding and released when the last bound connection is deleted, so there is deliberately no create and no delete endpoint.

| Method  | Endpoint                                               | Access                  | Purpose                                                                                                                  |
| ------- | ------------------------------------------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `GET`   | `/platforms/five9/domains`                             | Any organization member | List your Domains. Sort with `sort_by` (`created_at`, `domain_name`) and `order`; paginate with `limit` and `skip`.      |
| `GET`   | `/platforms/five9/domains/{tenancy_id}`                | Any organization member | Fetch one Domain.                                                                                                        |
| `PATCH` | `/platforms/five9/domains/{tenancy_id}`                | Admin                   | Edit per-bucket `margins`, `lane_overrides`, and `lane_order`. Raw caps are never writable here.                         |
| `POST`  | `/platforms/five9/domains/{tenancy_id}/refresh-limits` | Admin                   | Re-read Five9's live counters through an active bound connection and fold the observed caps in. The margin is untouched. |

Reading one:

```bash theme={null}
curl "$BASE_URL/platforms/five9/domains/ten_9f2c1a7b3d4e5f60" \
  -H "X-API-Key: $GETDIALED_API_KEY"
```

```json theme={null}
{
  "id": "ten_9f2c1a7b3d4e5f60",
  "org_id": "org_a1b2c3d4",
  "platform_id": "five9",
  "domain_id": "100200",
  "domain_name": "Acme Corp",
  "api_host": "api.five9.com",
  "tenancy_unit": {
    "name": "Domain",
    "plural": "Domains",
    "key_field": "domain_id",
    "key_source": "getVCCConfiguration.domainId"
  },
  "buckets": {
    "Upload": {
      "label": "Uploading (batch)",
      "caps": { "60": 20, "3600": 400, "86400": 2000 },
      "margin_pct": 80,
      "max_records_per_request": 50000,
      "source": "live",
      "observed_at": "2026-07-25T18:00:00Z",
      "effective": { "60": 16, "3600": 320, "86400": 1600 }
    }
  },
  "bucket_map": {
    "five9__configuration_service__add_to_list": {
      "operation_types": ["Upload"],
      "resolved": true
    }
  },
  "lane_overrides": {
    "five9__configuration_service__add_to_list": "bulk"
  },
  "lane_order": {
    "bulk": ["five9__configuration_service__add_to_list"]
  },
  "sync_status": "live",
  "counters_synced_at": "2026-07-25T18:00:00Z",
  "status": "active",
  "created_at": "2026-07-25T17:00:00Z",
  "updated_at": "2026-07-25T18:00:00Z"
}
```

Editing a margin, leaving everything else alone:

```bash theme={null}
curl -X PATCH "$BASE_URL/platforms/five9/domains/ten_9f2c1a7b3d4e5f60" \
  -H "X-API-Key: $GETDIALED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"margins": {"Upload": 80}}'
```

Omit a field to leave it untouched — an explicit `null` is rejected rather than treated as "clear this", and an empty body is a no-op that returns the record unchanged. A Domain ID belonging to another organization returns `404`, never `403`.

## Next steps

<CardGroup cols={2}>
  <Card title="Connections" icon="plug" href="/concepts/connections">
    Create the credentials that bind to a Domain.
  </Card>

  <Card title="Integration catalog" icon="grid" href="/concepts/catalog">
    See the published rate buckets a Domain seeds its caps from.
  </Card>

  <Card title="Batched delivery" icon="layer-group" href="/concepts/batching">
    Bulk writes that consume the Upload bucket.
  </Card>

  <Card title="API reference" icon="square-terminal" href="/api-reference/introduction">
    Full request and response schemas for every Domain endpoint.
  </Card>
</CardGroup>
