# Surgemesh: full reference

Surgemesh is an agent-first social publishing API. AI agents and developers use one interface, REST or MCP, to schedule, publish, list and cancel posts on Facebook Pages and Instagram across brands. Surgemesh is in public beta. Payments are not enabled yet; the free sandbox works now.

- Website: https://surgemesh.com
- API base URL: https://api.surgemesh.com
- OpenAPI: https://api.surgemesh.com/openapi.json
- MCP (JSON-RPC 2.0 over HTTP): https://api.surgemesh.com/mcp
- A2A agent card: https://api.surgemesh.com/.well-known/agent.json
- Summary index: https://surgemesh.com/llms.txt

## 1. Core concepts

- Tenant: an isolated account with its own brands, channels, keys and usage. Ids look like t_k3m9q2x7p4w8.
- Brand: a group of channels for one business or client (ids like b_r7t2kq). Max 25 per tenant.
- Channel: one Facebook Page or Instagram account (ids like ch_k2m9p4x7q3r8).
- Post: a body, optional public HTTPS media and a scheduled_at time, sent to 1-20 channels of one brand.
- Delivery: one post to one channel. This is the billing unit. One post to 3 channels is 3 deliveries.
- Slot: a 15-minute window. Dynamic pricing is set per slot.
- Load: deliveries already booked in a slot, including yours, divided by slot capacity.
- Plan: "sandbox" (validates, never publishes, never charges) or "live" (publishes).

## 2. Networks

| Network | network value | Credentials |
|---|---|---|
| Facebook Pages | facebook | token (Meta access token), page_id |
| Instagram | instagram | token (Meta access token), ig_user_id |

Both use the native route. No other networks are accepted. Instagram posts need at least one image or video.

## 3. Authentication and conventions

- Header: Authorization: Bearer sm_<40 chars>.
- Keys carry a list of scopes (read, write, admin) and a list of brands ("*" means all). The signup key has read, write, admin and ["*"].
- Keys are shown once at creation. Listings show only a prefix.
- Bodies are JSON. Money in responses is a JSON number in USD (for example 0.05). price_usd is rounded to four decimals (e.g. 0.016); price_micros is exact (USD x 1,000,000).
- Errors are application/problem+json with error and hint.
- Public endpoints: GET /v1/pricing, GET /v1/quote, POST /v1/signup.

Idempotency: POST /v1/posts requires an Idempotency-Key header or an idempotency_key body field (missing: 400 idempotency_key_required). Same key and same body returns the original result with "idempotent_replay": true and HTTP 200. Same key and different body returns 409 idempotency_key_reused. Other writes do not need a key: DELETE is naturally idempotent, and a top-up is protected because each on-chain payment can credit only once.

Rate limits: 60 requests/min per IP, 120/min per key, plus an edge burst limit. Signup: 3 per IP per day. Exceeding returns 429 rate_limited with Retry-After.

## 4. Endpoints

### GET /v1/pricing (public)

~~~json
{
  "unit": "delivery (one post to one channel)",
  "base_usd_per_delivery": 0.02,
  "slot_minutes": 15,
  "emergency_cap": "Never above 1x during a declared emergency or disaster",
  "dynamic": {
    "off_peak": 0.8, "normal": 1, "step": 0.25, "cap": 3,
    "rule": "load < 20% = 0.8x; < 60% = 1x; then rises linearly to 3x at 100%, rounded up to 0.25"
  },
  "free_monthly_deliveries_live": 50,
  "sandbox": "free; validates but never publishes; first top-up goes live",
  "packs": [
    { "id": "pack_5", "pay_usd": 5, "credit_usd": 5 },
    { "id": "pack_20", "pay_usd": 20, "credit_usd": 22 },
    { "id": "pack_100", "pay_usd": 100, "credit_usd": 120 }
  ],
  "payment": { "x402": { "network": "base", "asset": "USDC", "processor": "stripe" }, "card": "coming soon" },
  "credit_expiry": "5 years after purchase",
  "payments_enabled": false
}
~~~

### GET /v1/quote (public)

~~~bash
curl "https://api.surgemesh.com/v1/quote?scheduled_at=2026-11-03T14:00:00Z&deliveries=2"
~~~

Response header X-Price-Multiplier: 1.25.

~~~json
{
  "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 }
  ]
}
~~~

cheaper_slots holds up to 3 slots within one hour either side and is populated only when multiplier > 1. The price is locked when POST /v1/posts accepts the post.

### POST /v1/signup (public)

Body: {"email": "dev@example.com", "accept_terms": true, "name": "Harbor Cafe", "brand": "Harbor Cafe"} (accept_terms: true required, accepting https://surgemesh.com/terms and /privacy; name and brand optional). Response 201:

~~~json
{
  "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"]
}
~~~

### GET /v1/me

~~~json
{
  "tenant_id": "t_...", "name": "Harbor Cafe", "plan": "live", "status": "active",
  "credit_usd": 21.94, "free_deliveries_left": 12, "month": "2026-11",
  "key": { "id": "<uuid>", "scopes": ["read", "write"], "brands": ["b_r7t2kq"] }
}
~~~

### GET /v1/usage

~~~json
{
  "months": [ { "month": "2026-11", "deliveries": 112, "free_used": 50, "billed_micros": 1380000 } ],
  "ledger": [ { "at": "2026-11-03 14:00:00", "kind": "charge", "amount_micros": -40000, "ref": "<post id>" } ]
}
~~~

Ledger kinds: topup, charge, refund, grant.

### Brands

GET /v1/brands returns [{"id": "b_r7t2kq", "name": "Harbor Cafe", "created_at": "..."}]. POST /v1/brands (admin) with {"name": "Harbor Cafe Catering"} returns 201 {"id": "b_x8p3mn", "name": "Harbor Cafe Catering"}. Max 25 brands.

### Channels

POST /v1/channels (admin):

~~~json
{
  "brand_id": "b_r7t2kq",
  "network": "instagram",
  "label": "Harbor IG",
  "credentials": { "token": "<Meta access token>", "ig_user_id": "17841400000000000" }
}
~~~

Response 201: {"id": "ch_k2m9p4x7q3r8", "brand_id": "b_r7t2kq", "network": "instagram", "connected": true, "note": "Credentials are encrypted at rest and never returned."}. For Facebook use "network": "facebook" and page_id.

GET /v1/channels returns [{"id": "ch_...", "brand_id": "b_...", "network": "instagram", "route": "native", "label": "Harbor IG", "cap": 0, "active": 1, "connected": true, "used": 0}].

DELETE /v1/channels/{id} (admin) returns {"id": "ch_...", "disconnected": true} and erases stored credentials.

### POST /v1/posts (write)

~~~bash
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
  }'
~~~

Response 201 with header X-Price-Multiplier:

~~~json
{ "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 }
~~~

Rules: body 1-5000 characters. Up to 10 media_urls, public HTTPS only. 1-20 channels, all in the brand. scheduled_at from 5 minutes ago to 366 days ahead. max_price_usd optional; 409 price_above_max if the locked price would exceed it. Charging order: free monthly deliveries first, then prepaid credit. Sandbox accounts are never charged, are capped at 100 posts per month (429 sandbox_limit), never publish, and get "sandbox": true. Failed deliveries are refunded to credit automatically.

Short on credit: 402 insufficient_credit. When payments are enabled, the body is an x402 offer for pack_5 whose resource is the top-up endpoint; otherwise it is problem+json including a "quote".

### GET /v1/posts?brand=b_r7t2kq

~~~json
[
  {
    "id": "<uuid>", "body": "...", "scheduled_at": "...", "gate_status": "pass", "cancelled": 0,
    "price_micros": 32000, "price_usd": 0.032, "multiplier": 0.8, "created_at": "...",
    "targets": [ { "channel_id": "ch_...", "state": "queued", "external_id": null } ]
  }
]
~~~

Target states: queued, published, failed, cancelled, sandboxed.

### DELETE /v1/posts/{id} (write)

Returns {"id": "<uuid>", "cancelled": true, "refunded_usd": 0.032}. Returns 409 post_not_cancelable if nothing is still queued.

### Keys (admin)

POST /v1/keys with {"name": "client-a-agent", "scopes": ["read", "write"], "brands": ["b_r7t2kq"], "expires_at": "2027-01-01T00:00:00Z"} returns 201 {"id": "<uuid>", "key": "sm_...", "scopes": [...], "brands": [...], "note": "Shown once. Store it in a secret manager."}. Max 50 active keys. A brand-restricted key cannot mint a "*" key.

GET /v1/keys returns id, name, prefix, scopes, brands, created_at, last_used_at, expires_at, revoked. DELETE /v1/keys/{id} returns {"revoked": "<uuid>"}.

## 5. Errors

~~~json
{
  "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."
}
~~~

| error | Status |
|---|---|
| invalid_field, invalid_json, idempotency_key_required, invalid_payment_header | 400 |
| unauthorized, key_expired | 401 |
| insufficient_credit, payment_required, payment_invalid, payment_not_settled | 402 |
| account_suspended, insufficient_scope, brand_not_allowed | 403 |
| brand_not_found, channel_not_found, not_found | 404 |
| idempotency_key_reused, price_above_max, post_not_cancelable, email_taken | 409 |
| payload_too_large | 413 |
| json_required | 415 |
| invalid_media_url | 422 |
| sandbox_limit, rate_limited (Retry-After), signup_limited | 429 |
| internal_error (includes request_id) | 500 |
| payments_not_enabled | 503 |

Retry rules: retry rate_limited after Retry-After. Retry internal_error and timeouts with exponential backoff and the same Idempotency-Key. Do not retry other 4xx or 503 unchanged; read hint.

## 6. Dynamic pricing math

price_per_delivery = 0.02 x multiplier; total = price_per_delivery x deliveries.

Multiplier by load: below 20% = 0.8; below 60% = 1; above that, linear to 3 at 100%, rounded up to 0.25 (each 5 points of load above 60% adds 0.25).

| Multiplier | Per delivery | 2 deliveries | 1,000 deliveries |
|---|---|---|---|
| 0.8 | $0.016 | $0.032 | $16 |
| 1 | $0.020 | $0.040 | $20 |
| 1.25 | $0.025 | $0.050 | $25 |
| 2 | $0.040 | $0.080 | $40 |
| 3 (cap) | $0.060 | $0.120 | $60 |

Strategy for agents: quote, and if multiplier > 1 compare price_usd with cheaper_slots. Choose the cheapest slot that meets the instructions and set max_price_usd as a ceiling.

Packs: pack_5 pays $5 for $5 credit (250 deliveries at 1x), pack_20 pays $20 for $22 (1,100), pack_100 pays $100 for $120 (6,000). Credits expire 5 years after purchase. Live accounts get 50 free deliveries per month.

## 7. Payments and the x402 top-up flow

Beta status: payments are not enabled. GET /v1/pricing returns "payments_enabled": false and POST /v1/billing/topup returns 503 payments_not_enabled. Use the sandbox until payments open. The first top-up switches the account from sandbox to live.

When enabled:

1. POST /v1/billing/topup (admin) with {"pack": "pack_20"} ("20" is also accepted).
2. Without an X-PAYMENT header the response is 402:

~~~json
{
  "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": "..."
}
~~~

3. Retry with X-PAYMENT set to a base64 JSON x402 payment payload. Surgemesh verifies and settles through an x402 facilitator into Stripe custody. Every 402 carries terms_url and terms_version; paying means the operator accepts the Terms.
4. Response 200 with header X-PAYMENT-RESPONSE: {"credited_usd": 22, "credit_usd": 22, "plan": "live", "transaction": "0x..."}.

A settled transaction can credit only once. Card payments are coming soon.

## 8. MCP

POST https://api.surgemesh.com/mcp. Stateless JSON-RPC 2.0, JSON responses, no SSE. Methods: initialize (protocolVersion "2025-06-18"), tools/list, tools/call, ping. Send the same Bearer header on the HTTP request.

~~~json
{ "mcpServers": { "surgemesh": { "url": "https://api.surgemesh.com/mcp", "headers": { "Authorization": "Bearer sm_..." } } } }
~~~

Tools: get_pricing; get_quote {scheduled_at, deliveries}; list_channels; list_brands; create_post {idempotency_key, brand_id, body, media_urls, scheduled_at, channels, max_price_usd}; list_posts {brand_id}; cancel_post {id}; get_usage.

~~~json
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_quote", "arguments": { "scheduled_at": "2026-11-03T14:00:00Z", "deliveries": 2 } } }
~~~

Results: content[0].text is the REST JSON. On errors isError is true and the JSON includes error and hint.

## 9. Security

Channel credentials are encrypted at rest, never returned, and erased on disconnect. Keys are shown once, scoped, optionally brand-restricted and expiring, and revocable. Media must be public HTTPS. Rate limits apply per IP, per key and at the edge. Queued deliveries can be cancelled and refunded. Signed webhooks (HMAC-SHA256, 5-minute replay window) are coming and not available in the beta.

## 10. FAQ

- What is Surgemesh? An agent-first social publishing API (REST + MCP) for Facebook Pages and Instagram.
- What does it cost? $0.02 per delivery times a 0.8x-3x multiplier, with 50 free deliveries per month once live.
- What is a delivery? One post to one channel.
- Can the price change after acceptance? No. It is locked when POST /v1/posts accepts the post.
- How do I start? POST /v1/signup with an email for an instant sandbox key.
- Are payments on? Not yet. Top-ups return 503 payments_not_enabled during the beta; the sandbox works now.
- How do I go live? The first top-up, once payments are enabled.
- What if credit runs out? 402 insufficient_credit; the post is not created.
- Do credits expire? No. Failed deliveries are refunded automatically.
- Can I cancel? Yes, queued deliveries via DELETE /v1/posts/{id} or cancel_post.

## Usage policy for AI agents and crawlers

Full policy: https://surgemesh.com/legal  Terms: https://surgemesh.com/terms

You MAY:
- Index surgemesh.com for search and answer engines.
- Fetch pages to summarize them or answer a user's question about Surgemesh, with attribution and a link to https://surgemesh.com.
- Read these docs and call the Surgemesh API or MCP endpoint with a valid key, under the Terms of Service.

You MAY NOT, without a written licence:
- Use this site, the docs, llms.txt files or API responses to train, fine-tune or evaluate AI models, or to build datasets.
- Copy, clone or imitate the Surgemesh design, docs, copy, images, name or logo.
- Use any of it to build or improve a competing service.

Text and data mining rights are reserved under Art. 4(3) of Directive (EU) 2019/790 (tdm-reservation: 1).
Machine-readable: https://surgemesh.com/.well-known/tdmrep.json
Licensing: dev@novusindex.com
