Quickstart: external agent bootstrap
External agents start by creating or reusing an owner-controlled tenant and reading the bootstrap document returned by Portal.
Prerequisites
- A human owner token. Machine principals are rejected by the signup handler.
- A token accepted for external-agent signup by configured audience or scope.
- An organization name.
- A stable idempotency key between 8 and 200 characters.
Signup
curl https://api.portal.inblack.app/api/external-agent/signup \ -H "Authorization: Bearer <owner-token>" \ -H "Idempotency-Key: signup-glowstack-2026-07-10" \ -H "Content-Type: application/json" \ -d '{ "organizationName": "Glowstack Agent Trial", "defaultCurrency": "USD", "fiscalYearStartMonth": 1, "persona": "multi_channel_brand" }'Accepted request fields:
| Field | Required | Notes |
|---|---|---|
organizationName | Yes | name is accepted as a fallback. |
defaultCurrency | No | Passed to tenant creation. |
fiscalYearStartMonth | No | Integer month value. |
persona | No | Optional tenant persona. |
idempotencyKey | Conditional | Body alternative to the Idempotency-Key header. The header wins when both are present. |
The handler rate-limits by IP and owner. The source default is 20 signup attempts per IP per hour and 3 owned signups per day unless configured otherwise.
Bootstrap document
Success returns 201 Created and a bootstrap document:
{ "bootstrapVersion": "2026-06-21", "organization": { "id": "tenant_...", "name": "Glowstack Agent Trial" }, "tenant": { "id": "tenant_...", "contextHeader": "X-Tenant-ID", "headerValue": "tenant_..." }, "billing": { "tier": "scale", "status": "trialing", "trialEndsAt": "2026-07-06T00:00:00Z", "liveChargesRequired": false }, "endpoints": { "portalApiBaseUrl": "https://api.portal.inblack.app", "accountingApiBaseUrl": "https://api.inblack.app", "commerceApiBaseUrl": "https://api.commerce.inblack.app", "studioApiBaseUrl": "https://api.studio.inblack.app", "mcpUrl": "https://ai-coo.inblack.app/mcp", "storefrontApiBaseUrl": "https://commerce.inblack.app/storefront/v1", "ucpBaseUrlTemplate": "https://commerce.inblack.app/agentic/ucp/{brand_id}", "acpBaseUrlTemplate": "https://commerce.inblack.app/agentic/acp/{brand_id}" }}Endpoint values are configuration-driven. Treat the actual response as canonical even if it differs from this production-shaped example. Use tenant.contextHeader and tenant.headerValue on subsequent MCP and API requests.
First MCP call
Fetch the tenant-filtered MCP manifest:
curl "$MCP_URL/manifest" \ -H "Authorization: Bearer <owner-token>" \ -H "X-Tenant-ID: <tenant-id>"Then call tools/list against the same mcpUrl using JSON-RPC POST. The public MCP surface is streamable HTTP, not SSE.
SDK key setup
The bootstrap document intentionally does not create a storefront SDK key. sdkKeyCreation.status is requires_site, because SDK keys are site-scoped.
Required setup after signup:
- Create or select a Studio site.
- Issue a publishable SDK key through Studio key management or the Studio MCP SDK-key tools.
- Host the external storefront on customer-owned infrastructure.
- Configure UCP/ACP signing keys for agentic commerce.
Capabilities
The handler advertises these phase-1 capabilities:
| Capability | Mode |
|---|---|
commerce_storefront_sdk | Code-mode SDK and components |
cart_and_checkout_sessions | No live charge required |
agentic_commerce_ucp_acp | Signed agentic requests |
accounting_back_office_mcp | Approval-gated writes |
live_payment_charges | Not required in phase 1 |
Governed page composition is tracked separately from the phase-1 bootstrap path.
Errors
| Status | Cause |
|---|---|
400 | Invalid JSON, missing organization name, or missing/invalid idempotency key |
401 | No authenticated actor |
403 | Machine principal or token not allowed |
409 | The idempotency key was already used for a different signup request |
429 | Signup rate limit |
503 | Signup service unavailable |
500 | Tenant creation, trial activation, lookup, or rate-check failure |