Surgemesh API documentation

The Surgemesh API is a REST and MCP interface at https://api.surgemesh.com for scheduling, publishing, listing and cancelling social posts on Facebook Pages and Instagram. Authenticate with "Authorization: Bearer sm_...", send JSON, and send an Idempotency-Key header when you create a post.

Base URL and authentication

Public endpoints with no key are GET /v1/pricing, GET /v1/quote and POST /v1/signup.

Quickstart in 4 steps

1. Sign up and get a sandbox key

curl -X POST https://api.surgemesh.com/v1/signup \
  -H "Content-Type: application/json" \
  -d '{"email": "dev@example.com", "accept_terms": true, "name": "Harbor Cafe", "brand": "Harbor Cafe"}'
{
  "tenant_id": "t_k3m9q2x7p4w8",
  "plan": "sandbox",
  "brand_id": "b_r7t2kq",
  "api_key": "sm_...",
  "key_id": "<uuid>",
  "note": "Key shown once. Sandbox validates posts but never publishes. Buy any credit pack (POST /v1/billing/topup) to go live.",
  "next": ["GET /v1/me", "POST /v1/channels", "GET /v1/quote", "POST /v1/posts"]
}

The signup key has scopes read, write and admin and access to all brands (["*"]). name and brand are optional. accept_terms: true is required and accepts the Terms and Privacy Policy.

2. Connect a channel

curl -X POST https://api.surgemesh.com/v1/channels \
  -H "Authorization: Bearer sm_..." \
  -H "Content-Type: application/json" \
  -d '{
    "brand_id": "b_r7t2kq",
    "network": "instagram",
    "label": "Harbor IG",
    "credentials": { "token": "<Meta access token>", "ig_user_id": "17841400000000000" }
  }'
{
  "id": "ch_k2m9p4x7q3r8",
  "brand_id": "b_r7t2kq",
  "network": "instagram",
  "connected": true,
  "note": "Credentials are encrypted at rest and never returned."
}

For a Facebook Page, use "network": "facebook" and page_id instead of ig_user_id.

3. Get a quote

curl "https://api.surgemesh.com/v1/quote?scheduled_at=2026-11-03T14:00:00Z&deliveries=2"
{
  "slot": "2026-11-03T14:00:00.000Z",
  "deliveries": 2,
  "multiplier": 1.25,
  "price_micros": 50000,
  "price_usd": 0.05,
  "base_usd_per_delivery": 0.02,
  "cheaper_slots": [
    { "slot": "2026-11-03T14:30:00.000Z", "multiplier": 0.8, "price_usd": 0.032 },
    { "slot": "2026-11-03T13:45:00.000Z", "multiplier": 1, "price_usd": 0.04 }
  ]
}

4. Create the post in the cheaper slot

curl -X POST https://api.surgemesh.com/v1/posts \
  -H "Authorization: Bearer sm_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0d4b7f1e-5a2c-4e8b-b3f9-7c6a5d4e3f21" \
  -d '{
    "brand_id": "b_r7t2kq",
    "channels": ["ch_fb...", "ch_ig..."],
    "body": "Spring menu is live.",
    "media_urls": ["https://cdn.example.com/spring.jpg"],
    "scheduled_at": "2026-11-03T14:30:00Z",
    "max_price_usd": 0.10
  }'
{
  "id": "<uuid>",
  "gate_status": "pass",
  "sandbox": false,
  "slot": "2026-11-03T14:30:00.000Z",
  "multiplier": 0.8,
  "price_usd": 0.032,
  "free_deliveries_used": 0
}

The response is 201 with an X-Price-Multiplier header. From a sandbox account the same request returns "sandbox": true: it is validated, never published and never charged.

Post rules

List posts with GET /v1/posts?brand=b_r7t2kq. Each target has a state: queued, published, failed, cancelled or sandboxed.

MCP client configuration

The MCP server is stateless JSON-RPC 2.0 at POST https://api.surgemesh.com/mcp, with JSON responses (no SSE). It supports initialize (protocol version 2025-06-18), tools/list, tools/call and ping, and authenticates with the same Bearer header.

{
  "mcpServers": {
    "surgemesh": {
      "url": "https://api.surgemesh.com/mcp",
      "headers": { "Authorization": "Bearer sm_..." }
    }
  }
}
ToolArgumentsREST equivalent
get_pricingnoneGET /v1/pricing
get_quotescheduled_at, deliveriesGET /v1/quote
list_channelsnoneGET /v1/channels
list_brandsnoneGET /v1/brands
create_postidempotency_key, brand_id, body, media_urls, scheduled_at, channels, max_price_usdPOST /v1/posts
list_postsbrand_idGET /v1/posts
cancel_postidDELETE /v1/posts/{id}
get_usagenoneGET /v1/usage

In each tool result, content[0].text holds the REST JSON. On errors, isError is true and the JSON includes error and hint.

Endpoints

MethodPathAuthPurpose
GET/v1/pricingPublicBase price, dynamic pricing rule, emergency cap, packs, payment status
GET/v1/quotePublicPrice for scheduled_at and deliveries
POST/v1/signupPublicCreate a sandbox tenant, first brand and API key
GET/v1/meKeyPlan, status, credits, free deliveries left, key scopes
GET/v1/usageKeyMonthly deliveries and the credit ledger
GET/v1/brandsKeyList brands
POST/v1/brandsadminCreate a brand (max 25)
GET/v1/channelsKeyList channels
POST/v1/channelsadminConnect a Facebook Page or Instagram account
DELETE/v1/channels/{id}adminDisconnect a channel and erase its credentials
GET/v1/postsKeyList posts for a brand
POST/v1/postswriteCreate a post (Idempotency-Key required)
DELETE/v1/posts/{id}writeCancel queued deliveries and refund them
GET / POST/v1/keysadminList or create API keys (max 50 active)
DELETE/v1/keys/{id}adminRevoke a key
POST/v1/billing/topupadminBuy a credit pack via x402 (not enabled in beta)
POST/mcpKeyMCP JSON-RPC endpoint

Brands, channels and keys

A brand groups the channels of one business or client. A channel is one Facebook Page or one Instagram account. Create a brand with POST /v1/brands and {"name": "Harbor Cafe Catering"}, which returns 201 with {"id": "b_x8p3mn", "name": "Harbor Cafe Catering"}.

Mint a narrower key for each agent or client:

curl -X POST https://api.surgemesh.com/v1/keys \
  -H "Authorization: Bearer sm_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "client-a-agent", "scopes": ["read", "write"], "brands": ["b_r7t2kq"], "expires_at": "2027-01-01T00:00:00Z"}'
{
  "id": "<uuid>",
  "key": "sm_...",
  "scopes": ["read", "write"],
  "brands": ["b_r7t2kq"],
  "note": "Shown once. Store it in a secret manager."
}

A brand-restricted key cannot mint a "*" (all-brands) key. GET /v1/keys lists id, name, prefix, scopes, brands, created_at, last_used_at, expires_at and revoked, never the full key. DELETE /v1/keys/{id} returns {"revoked": "<uuid>"}.

Idempotency

POST /v1/posts requires an Idempotency-Key header, or an idempotency_key field in the body. Without it, the API returns 400 idempotency_key_required.

Other writes do not need a key. DELETE requests are naturally idempotent, and a top-up is protected because each on-chain payment can credit only once.

Errors

Errors use application/problem+json with a stable error code and a hint describing the next step. Validation errors add field:

{
  "type": "https://surgemesh.com/errors/invalid-field",
  "title": "Invalid field",
  "status": 400,
  "detail": "Invalid field: scheduled_at",
  "error": "invalid_field",
  "field": "scheduled_at",
  "hint": "Fix the field named in `field` and retry. Schemas: GET /openapi.json."
}
Statuserror codesWhat to do
400invalid_field, invalid_json, idempotency_key_required, invalid_payment_headerFix the request (see field) and resend
401unauthorized, key_expiredSend a valid, unexpired Bearer key
402insufficient_credit, payment_required, payment_invalid, payment_not_settledTop up or fix the x402 payment, then retry
403account_suspended, insufficient_scope, brand_not_allowedUse a key with the right scope or brand; do not retry unchanged
404brand_not_found, channel_not_found, not_foundCheck the id
409idempotency_key_reused, price_above_max, post_not_cancelable, email_takenRead hint; requote, change slot or read post state
413payload_too_largeSend a smaller body
415json_requiredSend Content-Type: application/json
422invalid_media_urlUse a public HTTPS URL
429rate_limited, sandbox_limit, signup_limitedFor rate_limited, wait Retry-After seconds
500internal_errorRetry with backoff; quote request_id if reporting
503payments_not_enabledPayments are off during the beta; keep using the sandbox

Rate limits

Exceeding a limit returns 429 rate_limited with a Retry-After header.

Top up credits with x402

Payments are not enabled during the beta. Today, POST /v1/billing/topup returns 503 payments_not_enabled. When payments are enabled, the flow is:

  1. Call POST /v1/billing/topup with {"pack": "pack_20"} ("20" is also accepted) using an admin key.
  2. Without an X-PAYMENT header, Surgemesh answers 402 with x402 requirements in accepts.
  3. Your x402 client signs a USDC payment on Base matching the requirement and sends it, base64-encoded, in X-PAYMENT.
  4. Surgemesh verifies and settles through an x402 facilitator into its Stripe deposit address, records the payment with Stripe, and answers 200 with an X-PAYMENT-RESPONSE header. A payment Stripe flags is held for review (202 payment_under_review).
  5. Every 402 carries terms_url and terms_version. Sending payment means the operator behind the agent accepts the Terms.
{
  "x402Version": 1,
  "error": "payment_required",
  "accepts": [{
    "scheme": "exact",
    "network": "base",
    "maxAmountRequired": "20000000",
    "resource": "https://api.surgemesh.com/v1/billing/topup#pack_20",
    "description": "Surgemesh credit pack_20: pay $20, get $22 credit",
    "mimeType": "application/json",
    "payTo": "0x...",
    "maxTimeoutSeconds": 300,
    "asset": "<USDC contract on Base>",
    "extra": { "name": "USD Coin", "version": "2" }
  }],
  "hint": "..."
}
{ "credited_usd": 22, "credit_usd": 22, "plan": "live", "transaction": "0x..." }

A settled transaction can credit only once. The first top-up switches the account from sandbox to live.

Machine-readable resources