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

# Storefront agents

> Browse the sales, signal, creative, and outcome agents registered to your storefront and run their OAuth flows

A **storefront agent** is an agent registered for use with your storefront — a
sales/media agent, a signal/data agent, a creative agent, or an outcome
measurement agent. Your organization can own the Agent (`SELF`) or use one from
the wider marketplace (`MARKETPLACE`). Each Agent also carries capability
metadata (`type`, `protocol`, `authenticationType`) and a lifecycle `status`.
List and inspect Agents to decide which ones power your storefront and which
need authorization.

Agents that use OAuth need an interactive consent flow before the platform can call them. There are two flows: **agent-level** OAuth stores tokens in the agent configuration for platform-to-agent calls, and **per-account** OAuth stores tokens against the buyer/operator account the grant represents. Both return an `authorizationUrl` you send the operator to.

All examples use the storefront base URL:

```
https://api.apostra.com/api/v2/storefront
```

Authenticate every request with `Authorization: Bearer $SCOPE3_API_KEY`.

<Note>
  Partner-agent endpoint URLs must be directly reachable public HTTPS endpoints.
  Private-network, localhost, DNS-rebound, and redirecting URLs are rejected
  when you create or update an agent. See [Public callback URL
  requirements](/v2/reference/public-callback-url-requirements).
</Note>

## Manage an Agent in Murph

**Agents** is a full workspace alongside Inventory and Advertisers. Choose
**Agents** in the left navigation to open the collection in the main canvas.
Select an Agent, then use its workspace navigation to move between:

* **Overview** for identity, ownership, current status, and the next action.
* **Endpoint & protocol** for the shared, non-secret endpoint contract.
* **Test Agent** for the next test action, its result, and Sales Agent
  certification status.
* **Inventory sources** for the exact Sales Agent deployments you can access.

Credentials belong to the exact Inventory Source and are never copied into the
Agent or its observed revision. Open that source from **Inventory sources** to
set up a supported connection. Other Agent roles show only the sections
supported by their authorized connection contract. See
[Agents](/v2/concepts/agents) for the ownership and navigation model.

## Prepare an Agent for testing

The page shows one next action for the Agent's current state. A new Agent first
needs an authorized Source connection with the endpoint and any required
authentication. When you choose **Test Agent**, the platform reads the current
observed protocol surface and tells you what still needs to happen before a
test can start.

The main test card shows the current outcome and the next useful action. Open
**Test details** or **Certification details** for the selected test mode,
technical target information, certification requirements, and policy details.

If the page says **Testing needs setup**, no test has run and no Agent check has
failed. The message identifies the next safe action:

* **Confirm test endpoint** means a directly authenticated Account Admin must
  confirm the exact endpoint for the currently registered observed revision.
  The confirmation does not deploy code, register a fixture, or certify the
  Agent.
* **Interchange test setup** means Interchange must review a distinct no-spend
  test environment and its current isolation evidence. Do not repurpose or
  archive a live Source to satisfy this step.

The page keeps a safe diagnostic reference with a not-started result when the
server supplies one. It does not show raw transport or provider error text.

## Run a no-spend transaction validation

Use **Run test** only when the page says the test can start. The page names the
current Source when that Source is the target. If the test environment is still
being prepared, it does not present a runnable test button. A test records
evidence; it does not deploy code or certify the Agent.

Only the claimed owner can confirm a test. A claimed owner, or Interchange
Support viewing an exact current Source, can start one. Governed administrators
can review the target, instructions, results, and certification state, but
cannot run or confirm the test.

The current Agent model does not represent separate serving and test targets in
the public Agent read. Do not infer that registering an observation promotes
new software or that a test is running against a different serving version.
Results are bound only to the target the current runner reports.

The page shows **Confirm sandbox step** before staging a media buy and again
before activating the sandbox campaign. Review each pending action, then
confirm it in that same Agent page. Each confirmation is bound to the exact
validation run, Agent revision, generation, and pending step; it cannot approve
a different run or a later retry. If a confirmation is expired, cancelled, or
has already been used, start a new validation instead of retrying the old
control.

After the confirmation, the run continues from its server-recorded checkpoint.
Its result identifies whether it completed, failed a check, or could not start,
and reports the safe run reference and cleanup outcome when available. A passed
test is evidence for the target it names, not a certificate or an authorization
for a client connection.

## Free certification

Technical testing and production certification are separate. The Agent page
shows remaining certification checks and any governed review without requiring a
publisher customer, live inventory, a payment card, or Partner Program
membership. Joining the Partner Program is a separate commercial action; it
does not repair a technical test prerequisite or grant certification.

For a Sales Agent, the **Catalog Product Quality** requirement also shows an
advisory count: how many of the Agent's products declare AdCP channels, counted
the same way as the storefront readiness warning **Product channels declared**.
It covers the active third-party Sources your organization can see for the
Agent. The count is guidance only. It never changes a certification result, and
it is omitted until a product from one of those Sources has been observed. The
count covers up to 50 Sources; when the Agent has more, the page says so under
the count. See
[Declare AdCP channels on every Product](/v2/storefront/inventory-sources/sales-agent-requirements#declare-adcp-channels-on-every-product).

## Provision a text-message line

A storefront Agent can be given its own SMS-capable US phone number. Apostra
buys the number from its carrier and binds it to that one Agent, so inbound
texts to it are routed to that Agent and messages sent from the line carry its
number.

Where Apostra has prepared the region for agent calling, it buys a number that
can carry calls as well as texts. That narrows what is on offer, so an area
code with plenty of texting numbers can still come back empty.

On such a number, calls reach the same Agent as texts. The line answers a
call, tells the caller they have reached an automated agent, and ends the
call; it does not hold a spoken conversation yet. Answering a call is billed by
the carrier as an answered call. See
[Agent voice lines](/v2/reference/agent-voice) for exactly what a caller hears
and what Apostra keeps. If Apostra cannot confirm the number's calling setup
with its carrier, the line still becomes `active` for texting, and Apostra
completes the calling setup separately.

This is available only to organizations Apostra has enabled for agent phone
lines. Until yours is enabled, a signed-in administrator's request answers
`404`.

### Who can request a line

Only a **signed-in seller administrator** can request a line. The request
spends money — it buys a telephone number from a carrier — so Apostra refuses
an API key, an M2M client, a service token, and any impersonated or simulated
session, and answers `ACCESS_DENIED`. Apostra staff helping you through
support access are refused too, even when support gives them administrator
rights in your account: only your own administrators buy numbers. The same
rule applies to reading a request's status. Make the call from your
authenticated Apostra session, not from a shared automation credential.

### Request the line

```http theme={null}
POST https://api.apostra.com/api/v2/communications/phone-lines
```

```json theme={null}
{
  "requestId": "cdb0f08c-b2fc-46bd-bf24-25142fb31f19",
  "storefrontAgentId": "storefront-AGENT_ID",
  "areaCode": "347",
  "maxQuotedUpfrontUsdCents": 500,
  "maxQuotedMonthlyUsdCents": 500
}
```

`requestId` is a UUID you choose; it makes the request replayable. `areaCode`
is a US area code. The two price fields are the highest **quoted** prices you
accept for the number.

<Warning>
  A price limit is a limit on the carrier's quote, not a cap the carrier
  enforces. Apostra reads the carrier's own price for the exact number
  immediately before it orders, and orders only while that price is at or below
  both of your limits and Apostra's own deployment limit. The carrier offers no
  maximum-price field on a number order, so a price change inside that last
  window is billed at the carrier's price. Apostra reports the price it read at
  the moment it ordered, so you can reconcile it against the invoice.
</Warning>

The request returns `202 Accepted`. It does not mean a number was bought.

### Follow the request

```http theme={null}
GET https://api.apostra.com/api/v2/communications/phone-lines/{requestId}
```

The `status` moves through `requested`, `ordering`, `order_pending`, and
`binding` before it reaches one of:

| Status | Meaning |
| - | - |
| `active` | The line is live. The response carries the assigned `phoneNumber` and its `endpointId`. |
| `failed` | No line was bought, or one could not be bound. `failureCode` says why — for example `no_number_within_quote_limit` when nothing suitable in that area code is offered at or below your limits, or `provider_order_failed` when the carrier declined the order. A request that keeps hitting a temporary problem before anything is ordered — the carrier is unavailable, or the number it offered stops being available — is retried for about ten minutes and then fails with the last `failureCode` it saw. Send a new `requestId` to try again. When a failed request still reports a `phoneNumber` (a number was bought but not bound, or the order never settled, `provider_order_pending_timeout`), an Apostra operator reconciles it before the agent can request another line. |
| `submission_unknown` | The carrier may hold a purchase Apostra could not confirm — no conclusive answer, or an order Apostra cannot match to this request. It is terminal, an Apostra operator reconciles it by hand, and it never retries — that is what stops a second number from being bought. |

`phoneNumber` is set only once the carrier has accepted an order for that
number. A request that fails before then reports no number and no quote.

Once a number is held, `quotedUpfrontUsdMicros` and `quotedMonthlyUsdMicros`
report the accepted price in USD micros (1,000,000 micros = 1 USD).

### Replays and conflicts

Reuse the same `requestId` with an identical body after a client timeout: the
replay returns the original request with `duplicate: true` and buys nothing.
Reusing a `requestId` with different input returns `CONFLICT`. So does a second
request for an Agent that already has a line, or an unresolved earlier request.

### Request a line from Distribution

Your Listing page shows a **Text** destination under Distribution once your
organization is enabled for agent phone lines. It covers your storefront's own
Agent and shows what to do next:

| The Text tab shows | What it means | What to do |
| - | - | - |
| **Add number** | The Agent has no line, or its last request ended without buying a number. | Enter a US area code and select **Add text number**. The tab shows Apostra's deployment price limits before you submit, and the request is held to them. |
| **Setting up** | A request is being ordered and bound (`requested`, `ordering`, `order_pending`, or `binding`). | Nothing. Setup continues after you close the page; check back in a few minutes. |
| **Ready** | The line is active. The tab shows its number. | Nothing. Texts to that number reach your storefront Agent. |
| **Needs attention** | The carrier may already have sold a number for this Agent: the request is `submission_unknown`, a bought number could not be bound, or an order never settled. | Contact Apostra support. An operator reconciles the order; another request for this Agent is refused until then. |

The same rules apply as for the API request: only a signed-in seller
administrator can add a number, and Apostra staff using support access cannot.
If the page loses its connection while you submit, select **Add text number**
again with the same area code. The page replays the original request instead of
buying a second number.

An agent can read the same state without opening the page:
`get({ kind: "distribution" })` returns it as `channels.text`, which is `null`
until your organization is enabled.

| Field | Type | Meaning |
| - | - | - |
| `storefrontAgentId` | string or null | The storefront Agent the line belongs to. `null` when the account has no active storefront. |
| `status` | string or null | The status of the Agent's line or request, from the table above. `null` when the Agent has no line and no request on record. |
| `requestId` | string or null | The request that bought or is buying the line. |
| `phoneNumber` | string or null | The number in E.164 format, once the carrier has accepted an order. |
| `failureCode` | string or null | Why the last request failed, when it did. |
| `canRequestLine` | boolean | Whether a new request for this Agent would be accepted. |
| `maxQuotedUpfrontUsdCents` | number | Apostra's deployment limit on the upfront quote, in US cents. |
| `maxQuotedMonthlyUsdCents` | number | Apostra's deployment limit on the monthly quote, in US cents. |
| `nextStep` | string | One sentence naming the next action, or that none remains. |

Requesting a number stays a page action. The model-visible tools never buy a
number.

## Task reference

<CardGroup cols={2}>
  <Card title="Create an Agent" href="/v2/storefront/agents/tasks/create-agent" icon="plus">
    `POST /agents` — register a private Agent for your organization
  </Card>

  <Card title="List agents" href="/v2/storefront/agents/tasks/list-agents" icon="list">
    `GET /agents` — registered agents, filterable by type, status, and
    relationship
  </Card>

  <Card title="Get agent" href="/v2/storefront/agents/tasks/get-agent" icon="magnifying-glass">
    `GET /agents/{agentId}` — full agent detail with account flags
  </Card>

  <Card title="Publish connection setup" href="/v2/storefront/agents/connection-setup" icon="plug-circle-plus">
    Define, preview, and publish the immutable non-secret contract publishers
    use
  </Card>

  <Card title="Start agent OAuth" href="/v2/storefront/agents/tasks/start-agent-oauth" icon="key">
    `POST /agents/{agentId}/oauth/authorize` — agent-level token flow
  </Card>

  <Card title="Start account OAuth" href="/v2/storefront/agents/tasks/start-account-oauth" icon="user-lock">
    `POST /agents/{agentId}/accounts/oauth/authorize` — per-account token flow
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.