Overview
Every seller on PairRail Atlas publishes one governed catalog. Agents browse, search and price it without credentials, and quote it with a Bearer key the seller approved. Quotes are indicative and non-binding until a person or the seller's published policy approves them and the seller's own billing runs the payment.
Public calls carry the seller's slug as sellerId. Keyed calls resolve the seller from the key, and a key only works for the seller that issued it.
The MCP endpoint (POST /mcp?sellerId=) exposes the same operations as JSON-RPC 2.0 tools. UCP, A2A, ACP and UAP / Beckn serve them in each protocol's own format, listed under Protocols. Guide: https://www.pairrail.com/docs
Endpoints
No credentials. Discovery and indicative pricing.
GET /v1/public/catalog
Published catalog for a seller
Public offers, access rules and how to request a key. Free and edge-cached; never counts against the seller's quota.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
POST /v1/public/discover
Match public offers to a buying need
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
| Field | Type | Notes |
|---|---|---|
need | string | What the buyer needs, in plain words. query is accepted as an alias. |
region | string | Only offers available in this region match. |
companySize | integer | Excludes offers whose minimum company size is larger. ≥ 1 |
{
"need": "inference",
"region": "US"
}200Matching offers, most relevant first. An empty need returns every public offer. → Envelope400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /v1/public/pricing/calculate
Indicative price at public rates
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
| Field | Type | Notes |
|---|---|---|
offerId required | string | Offer ID (or SKU) from the catalog. |
seats | integer | ≥ 1 |
units | number | Usage units for a usage meter (for example, million tokens). ≥ 0 |
commitmentMonths | integer | Must meet the offer's minimum commitment. ≥ 1 |
region | string | |
discountBps | integer | Requested discount in basis points. Outside the seller's fences it routes to approval or is refused. ≥ 0 |
input | object | Meter inputs by key (for example creditUnits) for offers with custom meters. |
{
"offerId": "off_northstar-compute_growth",
"seats": 5,
"commitmentMonths": 12,
"region": "US"
}200Itemized calculation and the policy decision → Envelope404Resource not found or not visible to this caller → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /v1/public/quote-verify
Check a signed quote or quote evidence bundle
Checks a compact JWS from a quote's signature.token, or from a seller's quote evidence export, against the keys at /.well-known/jwks.json. valid:true means PairRail signed exactly these facts. It does not say what the quote's status is now, and a signed quote is never a commitment (binding is false). An expired quote still verifies. The format is specified at /docs/quotes.
Auth No credentials
| Field | Type | Notes |
|---|---|---|
token required | string | Compact JWS (typ pairrail-quote+jws or pairrail-quote-evidence+jws). |
200Verification result. When valid is false, reason is malformed, unexpected_header, unknown_key or bad_signature and no claims are returned.400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error
Ask a seller for a key and pick it up once it is approved.
POST /v1/access-requests
Ask a seller for an agent key
Creates a pending request in the seller's inbox and returns a pickup token. A person on the seller's team approves or denies it. Free; never counted as a query. An optional Web Bot Auth signature is checked and shown to the seller with the request.
Auth No credentials
| Name | In | Notes |
|---|---|---|
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
sellerId required | string | |
agentName required | string | min length 2 |
contactEmail | string | |
clientId | string | max length 120 |
note | string | max length 500 |
quoteValidityWindow | string | max length 40 |
agentPublicKey | object | Optional public JWK held only by this agent (EC P-256, X25519, or RSA of 2048 to 8192 bits; never send the private key). The issued key and anything bought with it are then delivered encrypted to it as a compact JWE (ECDH-ES or RSA-OAEP-256, A256GCM), so agents relaying responses cannot read them. The seller sees its RFC 7638 thumbprint. |
identitySignals | object[] | Optional signals such as Web Bot Auth or Visa TAP tokens. Sellers see them as claimed; PairRail does not mark them verified. max 5 items |
{
"sellerId": "northstar-compute",
"agentName": "Acme procurement agent",
"contactEmail": "buyer@acme.example",
"clientId": "acme-agent-1",
"note": "Quoting GPU capacity for Q4"
}200A matching pending request already exists and is returned (reusedis true, no new pickup token). → AccessRequestCreated201Request created → AccessRequestCreated400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error
GET /v1/access-requests/{accessRequestId}
Poll a request and claim the key once
Authenticate with the pickup token in the Authorization header (a pickupToken query parameter is refused). When the request is approved the response carries the key in secret exactly once; later polls return alreadyDelivered and no secret. If the request registered an agentPublicKey, the key arrives in secretEncrypted instead, encrypted to that key. The key must be claimed within 14 days of approval; after that the poll returns 410 and the key is never delivered.
Auth Pickup token (Bearer pickup_…)
| Name | In | Notes |
|---|---|---|
accessRequestId required | path | string · e.g. acr_9b1d |
200Current status, and the key when it is first claimed → AccessRequestStatus400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error404Resource not found or not visible to this caller → Error410The claim window passed before the key was picked up (ACCESS_REQUEST_CLAIM_EXPIRED). Request access again. → Error
Seller-approved Bearer key (pat_…). Scope noted on each operation.
GET /v1/catalog
Catalog visible to this key
Scope: catalog:read. Includes offers the seller discloses only to customers or partners when the key has that access level.
Auth Agent key (Bearer pat_…)
GET /v1/catalog/offers
Offers visible to this key
Scope: catalog:read.
Auth Agent key (Bearer pat_…)
POST /v1/discover
Match visible offers to a buying need
Scope: catalog:read.
Auth Agent key (Bearer pat_…)
| Field | Type | Notes |
|---|---|---|
need | string | What the buyer needs, in plain words. query is accepted as an alias. |
region | string | Only offers available in this region match. |
companySize | integer | Excludes offers whose minimum company size is larger. ≥ 1 |
POST /v1/pricing/calculate
Price with this buyer's customer-tier rates
Scope: pricing:calculate. Deterministic and non-binding. Counts toward the key's pricing calls per hour (set by the seller), shared with MCP pricing_calculate calls made with the same key.
Auth Agent key (Bearer pat_…)
| Field | Type | Notes |
|---|---|---|
offerId required | string | Offer ID (or SKU) from the catalog. |
seats | integer | ≥ 1 |
units | number | Usage units for a usage meter (for example, million tokens). ≥ 0 |
commitmentMonths | integer | Must meet the offer's minimum commitment. ≥ 1 |
region | string | |
discountBps | integer | Requested discount in basis points. Outside the seller's fences it routes to approval or is refused. ≥ 0 |
input | object | Meter inputs by key (for example creditUnits) for offers with custom meters. |
200Itemized calculation and the policy decision. The decision says whether a quote would need approval, never the seller's approval caps. → Envelope401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error403The key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error
POST /v1/quote-requests
Create a non-binding quote
Scope: quote:request. Requires Idempotency-Key: retrying with the same key returns the original quote (200, replayed: true) instead of creating another. Above the seller's threshold the quote waits for a person on their deal desk. Splitting one order into smaller quotes does not avoid approval.
Auth Agent key (Bearer pat_…)
| Name | In | Notes |
|---|---|---|
Idempotency-Key required | header | Any unique string per intended quote. Reuse it when retrying. · string |
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
offerId required | string | Offer ID (or SKU) from the catalog. |
seats | integer | ≥ 1 |
units | number | Usage units for a usage meter (for example, million tokens). ≥ 0 |
commitmentMonths | integer | Must meet the offer's minimum commitment. ≥ 1 |
region | string | |
discountBps | integer | Requested discount in basis points. Outside the seller's fences it routes to approval or is refused. ≥ 0 |
input | object | Meter inputs by key (for example creditUnits) for offers with custom meters. |
need | string | What the buyer is trying to do, for the seller's deal desk. max length 500 |
buyer | object | Who you are buying for. Shown to the seller's deal desk as stated by you; never used for pricing or policy. |
{
"offerId": "off_northstar-compute_growth",
"seats": 25,
"commitmentMonths": 12,
"region": "US",
"need": "Team rollout in Q4",
"buyer": {
"company": "Acme Robotics",
"name": "Procurement agent",
"email": "procurement@acme.example"
}
}200Same Idempotency-Key; the original quote is returned → Envelope201Quote created → Envelope400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error403The key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error
GET /v1/quote-requests/{quoteRequestId}
Quote and approval status
Scope: quote:read. Poll this while a submitted quote waits on human approval. When the seller fulfills an executed quote later, the first read after that returns the credentials in data.entitlement, once; later reads show only its fingerprint and deliveredAt.
Auth Agent key (Bearer pat_…)
| Name | In | Notes |
|---|---|---|
quoteRequestId required | path | string |
POST /v1/quote-requests/{quoteRequestId}/submit
Send an approval-required quote to the seller's deal desk
Scope: quote:request. A quote over the seller's threshold comes back approval-required with review.submittedAt: null, and nobody at the seller sees it until the buyer agent calls this. Call it only when the buyer wants the quote at its price. It opens the seller's approval, sends the approval.requested webhook and tells the seller's team; then poll the quote for the decision. Calling it again returns the same quote with data.replayed: true. Also served on MCP as quote_submit and on UCP, ACP, A2A and UAP as the submit action (A2A skill quote.submit) with quoteId in the body.
Auth Agent key (Bearer pat_…)
| Name | In | Notes |
|---|---|---|
quoteRequestId required | path | string |
200Quote sent to the deal desk (or already sent); status stays approval-required until a person decides → Envelope401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error403The key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error404Resource not found or not visible to this caller → Error409The quote needs no approval or is finished (QUOTE_NOT_SUBMITTABLE), has expired (QUOTE_EXPIRED), or was priced on a catalog version that is no longer live (QUOTE_STALE). Request a new quote for the last two. → Error
POST /v1/quote-requests/{quoteRequestId}/execute
Hand an approved quote to the seller's billing
Scope: quote:execute, which sellers grant only on request. The quote must be within policy or approved by a person, and still match the live catalog, and the agent must present a delegated payment mandate its own human gave. Atlas refuses mandates that are expired, not yet valid, capped below the quote total, or bound to a different currency or quote. Mandates signed by an issuer the seller registered (ES256 on P-256, RS256 or EdDSA) are verified and must carry exp (at most 24 hours after issue), an integer maxAmountMinor and a jti unique per issuer. A mandate whose aud names another seller is refused (MANDATE_AUDIENCE_MISMATCH); sellers that require signed mandates also require aud (their seller id or slug) and quoteId. A mandate can name the agent allowed to spend it with azp set to its buyer key id (cred_…; other azp values, such as an OAuth client id, are ignored). A mandate signed by a registered issuer can also carry cnf: jkt, the RFC 7638 thumbprint of the agent's registered public key, or jwk, the key itself (cnf in unsigned tokens is ignored). Any other caller is refused (MANDATE_HOLDER_MISMATCH, MANDATE_HOLDER_KEY_UNKNOWN). Atlas then hands the quote to the seller's billing connector (Stripe, Chargebee or any billing system via signed webhook), which creates the invoice or quote there; the seller's processor stays the authority on the payment. The response has answerType: handed-off and data.handoff (provider, externalRef, status). Credentials the buyer bought are in data.entitlement and are returned only in this response: Atlas keeps a fingerprint, not the secret. When the buyer key has a registered agent key (or a signed mandate supplied cnf.jwk), data.entitlement.encrypted.jwe holds them encrypted to that key instead, and later quote reads return the same ciphertext.
Auth Agent key (Bearer pat_…)
| Name | In | Notes |
|---|---|---|
quoteRequestId required | path | string |
| Field | Type | Notes |
|---|---|---|
paymentMandate | string | object | A compact JWS (verified when the issuer is registered) or a mandate object. |
delegatedPaymentToken | string | A processor token or compact JWS. |
paymentMandateUri | string | uri |
maxAmountMinor | integer | Inline mandate cap in minor units (cents). ≥ 0 |
expiresAt | string | Inline mandate expiry. date-time |
azp | string | Inline mandates may name the buyer key (cred_…) allowed to spend them, like the azp claim of a signed mandate. |
200Quote handed off; the processor or webhook outcome is returned → Envelope401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error403The key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error404Resource not found or not visible to this caller → Error409The quote was already executed, or this signed mandate already paid for another quote (MANDATE_REPLAYED). → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error503The seller has not connected a processor or webhook yet (SETTLEMENT_NOT_CONFIGURED). Back off and retry. → Error
GET /v1/readiness
Seller readiness profile
Scope: readiness:read.
Auth Agent key (Bearer pat_…)
The same operations as JSON-RPC 2.0 tools.
GET /mcp
MCP server descriptor
The server's name, protocol versions and tools as JSON. A GET that accepts only text/event-stream gets 405: Atlas does not open a server-to-client stream. GET and POST are also served at /protocols/mcp.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
POST /mcp
MCP endpoint (JSON-RPC 2.0)
Tools: catalog_browse, catalog_discover, pricing_calculate, readiness_inspect and request_catalog_access need no credentials; quote_request needs a seller-approved Bearer key. An Idempotency-Key header (or an idempotencyKey argument) on quote_request is optional: reusing one returns the original quote, and without one every call creates a new quote. server/discover and tools/list are free handshakes, and so are initialize and ping on 2025-11-25 and earlier (2026-07-28 drops both). A tool that runs and fails returns a result with isError: true and the error code (not counted); a missing key, plan limit or rate limit is a JSON-RPC error. Protocol version 2026-07-28 requests carry params._meta and the MCP-Protocol-Version, Mcp-Method and Mcp-Name headers; initialize negotiates 2025-11-25 back to 2024-11-05. A key from another seller is refused on keyed tools (TENANT_DENIED) and ignored on public ones. A Web Bot Auth signature on quote_request or request_catalog_access is checked and recorded on the quote or access request.
Auth No credentials; Agent key (Bearer pat_…) for keyed tools
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
jsonrpc required | "2.0" | |
id | integer | string | |
method required | string | |
params | object |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "catalog_browse",
"arguments": {
"region": "US"
}
}
}GET /mcp/directory
Seller directory descriptor
The directory MCP server's name, endpoint, protocol versions and tools as JSON.
Auth No credentials
200Directory server descriptor
POST /mcp/directory
Seller directory (MCP, JSON-RPC 2.0)
One MCP server over the public catalogs of every seller who chose to list in the directory. Tools: directory_list (sellers by name, limit and cursor) and directory_search (need, optional region, companySize, limit; up to three matching public offers per seller). Each entry gives the seller's own mcpUrl: price, ask for access and quote there. Entries carry status (live or sandbox) and quotes (live or rehearsal). No key and no sellerId. Never counted against any seller's plan; rate limited per client. Sellers are listed by name, and search results by how well offer text matches need, never by price. The same MCP versions and handshakes as /mcp.
Auth No credentials
| Field | Type | Notes |
|---|---|---|
jsonrpc required | "2.0" | |
id | integer | string | |
method required | string | |
params | object |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "directory_search",
"arguments": {
"need": "H100 GPUs for three months",
"region": "US"
}
}
}The same catalog, prices and quotes on UCP, A2A, ACP and UAP / Beckn. Each rail needs the seller on Pro, and a seller can turn any of them off; MCP and REST stay on. Payloads follow each rail's own format, with PairRail's authority fields (binding, catalogVersion, policyVersion) kept. Browsers get a readable view of the GET descriptors; ask for application/json in the Accept header to get JSON.
GET /.well-known/ucp
UCP business profile
The seller's Universal Commerce Protocol profile: the shopping service endpoint (/ucp/{sellerId}) and the catalog search and lookup capabilities. Also served at /protocols/ucp.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
200UCP profile (ucp.services,ucp.capabilities)400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /ucp/{sellerId}/catalog/search
UCP catalog search
Public products whose content matches query, with variants and indicative prices in UCP's product shape. An empty query returns every public product. Also served at /ucp/catalog/search?sellerId=.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | path | The seller's slug, as the UCP profile's service endpoint names it. · string · e.g. northstar-compute |
| Field | Type | Notes |
|---|---|---|
query | string | Matched against product content. Empty returns every public product. |
{
"query": "growth"
}200UCP response withproducts;ucp.statusiserrorwhen nothing matches402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /ucp/{sellerId}/catalog/lookup
UCP catalog lookup
Products for each id: a product id or handle, or a variant id or sku, as search returns them. Ids with no match come back in messages with code not_found. Also served at /ucp/catalog/lookup?sellerId=.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | path | The seller's slug, as the UCP profile's service endpoint names it. · string · e.g. northstar-compute |
| Field | Type | Notes |
|---|---|---|
ids required | string[] | Product id or handle, or variant id or sku. |
{
"ids": [
"northstar-compute-compute-growth"
]
}200UCP response withproductsand anynot_foundmessages400No ids given (INVALID_INPUT) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /ucp/{sellerId}/catalog/product
UCP product
One product by a product id or handle, or a variant id or sku. With no match, the response has a not_found message instead of product. Also served at /ucp/catalog/product?sellerId=.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | path | The seller's slug, as the UCP profile's service endpoint names it. · string · e.g. northstar-compute |
| Field | Type | Notes |
|---|---|---|
id required | string | Product id or handle, or variant id or sku. |
{
"id": "northstar-compute-compute-growth"
}200UCP response withproduct, or anot_foundmessage400No id given (INVALID_INPUT) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /ucp/shopping
UCP shopping actions
Runs a PairRail action over UCP. The body is the protocol action envelope; with no action it prices. discover, price and readiness need no key. quote and execute need a seller-approved key with the same scope as REST (quote:request, quote:execute), and quote needs an Idempotency-Key header or arguments.idempotencyKey. A new quote is 201; a retry that returns an earlier quote is 200. Also served at /protocols/ucp/shopping.
Auth No credentials; Agent key (Bearer pat_…) for keyed actions
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
Idempotency-Key | header | Required for quote unless arguments.idempotencyKey is set. Reuse it when retrying. · string |
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
action | string | discover, price, readiness, quote or execute. Also read from skill, operation or intent. Aliases: search and catalog.discover discover; select and pricing.calculate price; init and quote.request quote; confirm, checkout and quote.execute execute. Omitted, the rail's default runs (price). |
arguments | object | The same fields as the REST call for that action (DiscoverInput, PricingInput, the quote body, or ExecuteInput plus quoteId). args, input and params are accepted too; with no wrapper, top-level fields are used. |
quoteId | string | The quote to execute, here or in arguments. |
{
"action": "price",
"arguments": {
"offerId": "off_northstar-compute_growth",
"seats": 25,
"commitmentMonths": 12,
"region": "US"
}
}200The action payload withprotocolandactionadded; otherwise the same envelope REST returns for that action → Envelope201Quote created → Envelope400Missing or malformed input (SELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED,QUOTE_ID_REQUIRED, orPROTOCOL_ACTION_UNKNOWNwith the supported actions indetails) → Error401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED), or the key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error404Resource not found or not visible to this caller → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
GET /protocols/ucp/products
UCP product feed
Every public product with its variants and indicative prices, in UCP's product shape.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
200Product feed (products,catalogVersion,binding)400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
GET /.well-known/agent-card.json
A2A agent card
The seller's A2A v1.0 agent card: the JSON-RPC interface URL, the skills (catalog.discover, pricing.calculate, readiness.inspect, quote.request, quote.execute) and capabilities (no streaming, no push notifications). Also served at /.well-known/agent.json, GET /a2a and /protocols/a2a.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
200A2A agent card400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /a2a
A2A JSON-RPC (SendMessage)
A2A v1.0 JSON-RPC. SendMessage runs the skill named in a data part (skill plus arguments); a message with only a text part runs catalog.discover with the text as the need. The answer is a ROLE_AGENT message whose data part is the same payload REST returns, with the REST status in metadata.status; HTTP is 200 on success.
quote.request and quote.execute need a seller-approved key. A quote uses a2a_ plus the messageId as its idempotency key unless arguments.idempotencyKey is set, so a retried message returns the same quote. Atlas creates no tasks: GetTask and CancelTask return -32001 (task not found), and streaming, task lists and push notifications are unsupported. Send A2A-Version: 1.0; a 0.3 slash-style method gets -32009. Errors use JSON-RPC codes with the PairRail error in error.data.
A body without jsonrpc is read as the protocol action envelope, as on /ucp/shopping. Also served at /protocols/a2a.
Auth No credentials; Agent key (Bearer pat_…) for keyed actions
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
A2A-Version | header | 1.0. Also accepted as the A2A-Version query parameter. A v1.0 method name with no version is served as 1.0. · string · e.g. 1.0 |
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
jsonrpc required | "2.0" | |
id | integer | string | |
method required | string | SendMessage. Task, streaming and push notification methods return JSON-RPC errors. |
params | object | message with a messageId and at least one part. A data part carries skill (catalog.discover, pricing.calculate, readiness.inspect, quote.request or quote.execute) and arguments. |
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-1",
"role": "ROLE_USER",
"parts": [
{
"data": {
"skill": "pricing.calculate",
"arguments": {
"offerId": "off_northstar-compute_growth",
"seats": 25,
"commitmentMonths": 12,
"region": "US"
}
}
}
]
}
}
}200JSON-RPC result (result.message) or, for the envelope, the action payload400JSON-RPC error for bad params, an unknown skill or method, or an unsupported version401JSON-RPC error;error.data.codeisAUTH_REQUIREDorINVALID_CREDENTIAL402JSON-RPC error;error.data.codeisPROTOCOL_NOT_ALLOWED(seller on Sandbox)403JSON-RPC error;error.data.codeisPROTOCOL_DISABLED,INSUFFICIENT_SCOPEorTENANT_DENIED404JSON-RPC error for task lookups (-32001), or an unknown seller or offer429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503JSON-RPC error;error.data.codeisAGENT_ACCESS_PAUSED
GET /protocols/acp
ACP descriptor
Agentic Commerce Protocol (2026-04-17) line items for each public offer at its indicative unit price. PairRail detail that ACP's LineItem has no field for (meters, tiers) is in pairrail.line_items. Also lists the PairRail endpoints for pricing and quotes. Atlas does not run ACP checkout sessions: quotes and execution go through PairRail actions.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
200ACP descriptor (line_items,offers,endpoints,authority)400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /protocols/acp
ACP actions
Runs a PairRail action over ACP, with the same envelope, keys and status codes as /ucp/shopping. With no action it prices. checkout and confirm are read as execute.
Auth No credentials; Agent key (Bearer pat_…) for keyed actions
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
Idempotency-Key | header | Required for quote unless arguments.idempotencyKey is set. Reuse it when retrying. · string |
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
action | string | discover, price, readiness, quote or execute. Also read from skill, operation or intent. Aliases: search and catalog.discover discover; select and pricing.calculate price; init and quote.request quote; confirm, checkout and quote.execute execute. Omitted, the rail's default runs (price). |
arguments | object | The same fields as the REST call for that action (DiscoverInput, PricingInput, the quote body, or ExecuteInput plus quoteId). args, input and params are accepted too; with no wrapper, top-level fields are used. |
quoteId | string | The quote to execute, here or in arguments. |
{
"action": "discover",
"arguments": {
"need": "compute"
}
}200The action payload withprotocolandactionadded; otherwise the same envelope REST returns for that action → Envelope201Quote created → Envelope400Missing or malformed input (SELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED,QUOTE_ID_REQUIRED, orPROTOCOL_ACTION_UNKNOWNwith the supported actions indetails) → Error401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED), or the key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error404Resource not found or not visible to this caller → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
GET /protocols/uap
UAP / Beckn descriptor
A Beckn-style provider descriptor: the subscriber, items with indicative prices, and which actions are public (search, select) and which need a seller-approved key (init, confirm). Also served at /protocols/beckn.
Auth No credentials
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
200UAP descriptor (subscriber,items,accessModel,endpoints)400Missing or malformed input (for exampleSELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED);detailslinks the seller's MCP endpoint → Error404Resource not found or not visible to this caller → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
POST /protocols/uap
UAP / Beckn actions
Runs a PairRail action with Beckn action names: search discovers, select prices (the default), init quotes and confirm executes. The body is the protocol action envelope, not a Beckn context and message. Keys and status codes are as on /ucp/shopping. Also served at /protocols/beckn.
Auth No credentials; Agent key (Bearer pat_…) for keyed actions
| Name | In | Notes |
|---|---|---|
sellerId required | query | The seller's slug, from Agent gateway → Protocol rails in their workspace. · string · e.g. northstar-compute |
Idempotency-Key | header | Required for quote unless arguments.idempotencyKey is set. Reuse it when retrying. · string |
Signature | header | Optional Web Bot Auth (RFC 9421 HTTP Message Signatures, tag web-bot-auth). Atlas verifies it against the key directory named in Signature-Agent and records the result (verified, invalid or unverifiable) on the quote (channel.signature) or access request (agentSignature). Record only; it never blocks a call or changes a price. · string |
Signature-Input | header | The signature parameters for Signature (covered components, created, expires, keyid, tag). @authority must be the host the request was sent to. · string |
Signature-Agent | header | Where the agent's public keys are published (a key directory URL). · string |
| Field | Type | Notes |
|---|---|---|
action | string | discover, price, readiness, quote or execute. Also read from skill, operation or intent. Aliases: search and catalog.discover discover; select and pricing.calculate price; init and quote.request quote; confirm, checkout and quote.execute execute. Omitted, the rail's default runs (price). |
arguments | object | The same fields as the REST call for that action (DiscoverInput, PricingInput, the quote body, or ExecuteInput plus quoteId). args, input and params are accepted too; with no wrapper, top-level fields are used. |
quoteId | string | The quote to execute, here or in arguments. |
{
"action": "select",
"arguments": {
"offerId": "off_northstar-compute_growth",
"seats": 25,
"commitmentMonths": 12,
"region": "US"
}
}200The action payload withprotocolandactionadded; otherwise the same envelope REST returns for that action → Envelope201Quote created → Envelope400Missing or malformed input (SELLER_ID_REQUIRED,IDEMPOTENCY_REQUIRED,QUOTE_ID_REQUIRED, orPROTOCOL_ACTION_UNKNOWNwith the supported actions indetails) → Error401Missing, invalid, expired or revoked credential (AUTH_REQUIRED,INVALID_CREDENTIAL) → Error402The seller is on Sandbox, which serves MCP and REST only (PROTOCOL_NOT_ALLOWED) → Error403The seller turned this rail off (PROTOCOL_DISABLED), or the key lacks the scope (INSUFFICIENT_SCOPE) or belongs to another seller (TENANT_DENIED) → Error404Resource not found or not visible to this caller → Error422Commercial validation failed (for exampleINVALID_COMMITMENT,POLICY_DENIED,QUOTE_NOT_EXECUTABLE) → Error429Too many requests, or a key's velocity limit was reached. HonorRetry-After.VELOCITY_LIMITdetails name the window and never give counts, limits or amounts. → Error503The seller paused agent access (AGENT_ACCESS_PAUSED) → Error
DiscoverInput
| Field | Type | Notes |
|---|---|---|
need | string | What the buyer needs, in plain words. query is accepted as an alias. |
region | string | Only offers available in this region match. |
companySize | integer | Excludes offers whose minimum company size is larger. ≥ 1 |
PricingInput
| Field | Type | Notes |
|---|---|---|
offerId required | string | Offer ID (or SKU) from the catalog. |
seats | integer | ≥ 1 |
units | number | Usage units for a usage meter (for example, million tokens). ≥ 0 |
commitmentMonths | integer | Must meet the offer's minimum commitment. ≥ 1 |
region | string | |
discountBps | integer | Requested discount in basis points. Outside the seller's fences it routes to approval or is refused. ≥ 0 |
input | object | Meter inputs by key (for example creditUnits) for offers with custom meters. |
AccessRequestInput
| Field | Type | Notes |
|---|---|---|
sellerId required | string | |
agentName required | string | min length 2 |
contactEmail | string | |
clientId | string | max length 120 |
note | string | max length 500 |
quoteValidityWindow | string | max length 40 |
agentPublicKey | object | Optional public JWK held only by this agent (EC P-256, X25519, or RSA of 2048 to 8192 bits; never send the private key). The issued key and anything bought with it are then delivered encrypted to it as a compact JWE (ECDH-ES or RSA-OAEP-256, A256GCM), so agents relaying responses cannot read them. The seller sees its RFC 7638 thumbprint. |
identitySignals | object[] | Optional signals such as Web Bot Auth or Visa TAP tokens. Sellers see them as claimed; PairRail does not mark them verified. max 5 items |
AccessRequestCreated
| Field | Type | Notes |
|---|---|---|
id | string | |
status | string | one of pending, approved, denied, expired |
pickupToken | string | null | |
pollUrl | string | uri |
reused | boolean | |
expiresAt | string | date-time |
AccessRequestStatus
| Field | Type | Notes |
|---|---|---|
id | string | |
status | string | one of pending, approved, denied, expired |
pending | boolean | |
secret | string | null | The agent key (pat_…). Present exactly once, on the first poll after approval. Null when the request registered an agentPublicKey. |
secretEncrypted | string | Present instead of secret when the request registered an agentPublicKey. It holds the agent key as a compact JWE encrypted to that key. |
alreadyDelivered | boolean | |
credential | object | null |
ExecuteInput
Send one form of delegated payment mandate.
| Field | Type | Notes |
|---|---|---|
paymentMandate | string | object | A compact JWS (verified when the issuer is registered) or a mandate object. |
delegatedPaymentToken | string | A processor token or compact JWS. |
paymentMandateUri | string | uri |
maxAmountMinor | integer | Inline mandate cap in minor units (cents). ≥ 0 |
expiresAt | string | Inline mandate expiry. date-time |
azp | string | Inline mandates may name the buyer key (cred_…) allowed to spend them, like the azp claim of a signed mandate. |
JsonRpcRequest
| Field | Type | Notes |
|---|---|---|
jsonrpc required | "2.0" | |
id | integer | string | |
method required | string | |
params | object |
Envelope
| Field | Type | Notes |
|---|---|---|
requestId required | string | |
traceId | string | |
seller | object | |
catalogVersionId | string | The exact approved catalog version this answer came from. |
catalogVersion | integer | |
policyVersionId | string | |
policyVersion | integer | |
answerType | string | For example informational, indicative or approval-required. |
binding required | false | |
authority | object | The seller's published quote authority (quote mode, binding rules, disclaimer). |
validFrom | string | date-time |
validUntil | string | date-time |
data required | any | Operation-specific response data. |
Error
| Field | Type | Notes |
|---|---|---|
requestId | string | |
error required | object |
UcpSearchInput
| Field | Type | Notes |
|---|---|---|
query | string | Matched against product content. Empty returns every public product. |
UcpLookupInput
| Field | Type | Notes |
|---|---|---|
ids required | string[] | Product id or handle, or variant id or sku. |
UcpProductInput
| Field | Type | Notes |
|---|---|---|
id required | string | Product id or handle, or variant id or sku. |
ProtocolAction
The PairRail action envelope that /ucp/shopping, /protocols/acp, /protocols/uap and a non-JSON-RPC /a2a body take.
| Field | Type | Notes |
|---|---|---|
action | string | discover, price, readiness, quote or execute. Also read from skill, operation or intent. Aliases: search and catalog.discover discover; select and pricing.calculate price; init and quote.request quote; confirm, checkout and quote.execute execute. Omitted, the rail's default runs (price). |
arguments | object | The same fields as the REST call for that action (DiscoverInput, PricingInput, the quote body, or ExecuteInput plus quoteId). args, input and params are accepted too; with no wrapper, top-level fields are used. |
quoteId | string | The quote to execute, here or in arguments. |
A2aJsonRpcRequest
| Field | Type | Notes |
|---|---|---|
jsonrpc required | "2.0" | |
id | integer | string | |
method required | string | SendMessage. Task, streaming and push notification methods return JSON-RPC errors. |
params | object | message with a messageId and at least one part. A data part carries skill (catalog.discover, pricing.calculate, readiness.inspect, quote.request or quote.execute) and arguments. |