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

# Accounts & Hierarchy

> The parent → child account model, external IDs, pooled billing, and how partner API keys are scoped and issued.

A partner integration is a two-level hierarchy: your **parent** account owns a set of **child** accounts. Each child is a full Trackly SMS account — typically one per business, brand, or location that you manage. The parent creates children, issues their API keys, controls their lifecycle (suspend / reactivate), and can read messages and billing across the whole portfolio.

## The parent account

Your parent account is a normal account that has children attached to it. Parent-level operations require a **parent key** and are refused for a key that belongs to a child (`403 not_a_parent_account`). A child account cannot itself have children — the hierarchy is exactly one level deep.

<Note>
  Sandbox (test) keys are **read-only** for the hierarchy: any write — creating a child, minting a key, suspending — is refused with `403 sandbox_read_only`. Provision your fleet with a live parent key.
</Note>

## External IDs

Every child carries an optional `externalIds` object so you can key Trackly accounts to your own system without maintaining a separate mapping table:

| Field        | Meaning                                                                               |
| ------------ | ------------------------------------------------------------------------------------- |
| `partnerId`  | Your identifier for the partner/tenant (usually constant across a parent's children). |
| `businessId` | Your identifier for the business that owns the location.                              |
| `locationId` | Your identifier for the specific location — **globally unique within a parent**.      |

Rules:

* Each value is a non-empty printable-ASCII string, **≤128 characters**.
* **`locationId` is unique per parent.** Creating a second child with a `locationId` already in use returns `409 duplicate_location` (with the conflicting `existingAccountId`), so you can safely retry provisioning without creating duplicates.
* **External IDs are immutable once set.** You may backfill a field that was never set via [update](/api-reference/v2/accounts/update-child), but changing or clearing a value that already exists returns `409 external_id_immutable`.

External IDs flow through the rest of the API: you can filter [message reads](/api-reference/v2/messages/list-messages) by `external_location_id`, and they are included on the [webhook envelope](/api-reference/v2/webhooks/signing) so your receiver can route an event to the right tenant.

## Billing model

How a child pays is decided at creation from two fields:

|    `paidIntent`   | `billedByParent` | Result                                                                                                                                                                                         |
| :---------------: | :--------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `false` (default) |    *(ignored)*   | Child is created on the **free** plan and pays for itself. `billedByParent` is only honored when `paidIntent` is `true`.                                                                       |
|       `true`      | `true` (default) | Child is **pooled** under the parent — its usage bills to the parent's payment method. The parent must have a payment method on file or creation returns `409 parent_payment_method_required`. |
|       `true`      |      `false`     | Child is created on the **paid** plan but self-pays.                                                                                                                                           |

<Note>
  Because `billedByParent` only takes effect when `paidIntent` is `true`, always send `paidIntent: true` explicitly when you want the parent to pool a child's billing. A child created without `paidIntent` is a free, self-paying account regardless of `billedByParent`.
</Note>

For a pooled child, cost is managed entirely by the parent: [preflight](/api-reference/v2/messages/preflight) returns `estimated_cost: null` for pooled children, and portfolio spend is read from the [usage & billing](/api-reference/v2/usage/usage-summary) endpoints on the parent.

## API keys

Each child gets its own API key(s), minted by the parent. Keys are **scoped**: you grant a list of `resource.action` permissions, and a child key can never exceed the grant of the parent key that created it. A set of sensitive resources (`admin`, `billing`, `account`, `accounts`, `users`, `keys`, and others) can never be granted, and the wildcard `*` is reserved.

* The **plaintext key is returned exactly once**, at creation or rotation. Store it immediately — it cannot be retrieved later, only rotated or revoked.
* **Rotation** on the child-key management surface is immediate: the replacement is minted and the old key is revoked with no grace window. (The separate [webhook signing-secret rotation](/api-reference/v2/webhooks/signing) does keep a 24-hour grace window — that's a different secret.)
* A key carrying IP allow-lists or resource constraints cannot mint further keys (`403 constrained_key_cannot_delegate`).

See [Manage API keys](/api-reference/v2/accounts/create-api-key) for the endpoints.

## Limits

| Limit                     | Value            |
| ------------------------- | ---------------- |
| Children per parent       | 100              |
| Child creation rate       | 100 / hour       |
| Active API keys per child | 25               |
| Child name length         | ≤ 255 characters |
| External-ID value length  | ≤ 128 characters |

Exceeding a cap returns a `4xx` with a specific `code` (`children_cap_exceeded`, `key_cap_exceeded`, `rate_limited` with a `Retry-After` header). See each endpoint page for the full list.

## Next steps

<CardGroup cols={2}>
  <Card title="Create a child" icon="plus" href="/api-reference/v2/accounts/create-child">
    Provision a new business account.
  </Card>

  <Card title="Mint an API key" icon="key" href="/api-reference/v2/accounts/create-api-key">
    Issue a scoped key for a child.
  </Card>
</CardGroup>
