Surgemesh

Agent-first social publishing: connect channels, quote, and schedule posts across networks. Remote streamable-HTTP MCP server; free API key via POST https://api.surgemesh.com/v1/signup. By Novus Index LLC.

होस्ट किया गया MCP सर्वर

npx add-mcp 'https://api.surgemesh.com/mcp'

Claude Code, Codex, Cursor और अन्य में इंस्टॉल होता है

दस्तावेज़

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, Instagram and Bluesky (more networks listed in GET /v1/networks). Authenticate with "Authorization: Bearer sm_...", send JSON, and send an Idempotency-Key header when you create a post.

Base URL and authentication

  • Base URL: https://api.surgemesh.com
  • Auth header: Authorization: Bearer sm_... (the key is sm_ followed by 40 characters).
  • Bodies: JSON. Money in responses is a JSON number in USD, for example 0.05.
  • Idempotency: POST /v1/posts requires an Idempotency-Key header (or an idempotency_key body field). Other writes do not.
  • Errors: application/problem+json with error and hint fields.
  • Spec: https://api.surgemesh.com/openapi.json.

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>",
  "verification": "unverified",
  "note": "Key shown once. Sandbox works now: it validates and prices posts but never publishes. To go live, verify ownership once (GET /v1/onboarding lists the steps), then add credit.",
  "next": [{"method": "GET", "href": "/v1/onboarding"}, {"method": "POST", "href": "/v1/channels"}, {"method": "GET", "href": "/v1/quote"}, {"method": "POST", "href": "/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"
{
  "scheduled_at": "2026-11-03T14:00:00.000Z",
  "deliveries": 2,
  "price_usd": <exact price for this time>,
  "price_micros": <the same price in millionths of a dollar>,
  "cheaper_times": [
    { "scheduled_at": "<a nearby time that costs less>", "price_usd": <its exact price> }
  ]
}

4. Create the post at the cheaper time

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>",
  "status": "accepted",
  "sandbox": false,
  "scheduled_at": "2026-11-03T14:30:00.000Z",
  "price_usd": <the locked price>,
  "charged_usd": <the same amount>,
  "cancelled": false
}

The response is 201. From a sandbox account (or to a sandbox test channel) the same request returns "sandbox": true with the same price_usd and "charged_usd": 0: it is validated exactly like a live post, never published and never charged. All timestamps are ISO 8601 UTC with Z.

Post rules

  • body: 1 to 5,000 characters, and within each target network's own limit (Bluesky 300 graphemes, Instagram 2,200 characters with at most 30 hashtags and 20 mentions). Sandbox and live check the same limits; GET /v1/networks lists them.
  • media_urls: up to 10, public HTTPS only, within each network's count, type and size limits (each URL is checked with HEAD). Instagram needs at least one image (JPEG) or video.
  • channels: 1 to 20 channel ids, all belonging to brand_id.
  • scheduled_at: ISO 8601 (e.g. 2026-11-03T14:30:00Z), in the future (5 minutes of grace) and at most 366 days ahead.
  • max_price_usd: optional. If the locked price would exceed it, the API returns 409 price_above_max with cheaper_times.
  • Charging: the locked price is debited from prepaid credit when the post is accepted. Failed deliveries are refunded to credit automatically.

List posts with GET /v1/posts?brand_id=b_r7t2kq. Each post has a status (pending while awaiting review, accepted, rejected or cancelled), boolean cancelled and sandbox, price_usd and charged_usd, and each target a state: queued, scheduled, processing, published, failed, cancelled or sandboxed.

MCP client configuration

The MCP server speaks 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
get_onboardingnoneGET /v1/onboarding
start_email_verification / confirm_email_verificationverification_id, codePOST /v1/verify/email, /confirm
start_dns_verification / check_dns_verificationdomain, verification_idPOST /v1/verify/dns, /check
request_owner_approval / get_verificationnote, callback_url, verification_idPOST /v1/verify/approval, GET /v1/verify/{id}
rotate_my_keygrace_secondsPOST /v1/keys/self/rotate
get_analyticsfrom, to, granularity, brand_idGET /v1/analytics

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, time-of-day pricing, emergency pledge, 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, key scopes, settings
POST/v1/me/settingsadminAccount settings: {"trend_opt_out": true} opts out of any future aggregated trend statistics (see Terms)
GET/v1/usageKeyMonthly deliveries and the credit ledger
GET/v1/brandsKeyList brands
POST/v1/brandsadminCreate a brand (max 25)
GET/v1/channelsKeyList channels
GET/v1/networksNoneEvery network: status, how to connect, review limits, media limits
POST/v1/channelsadminConnect a Facebook Page, Instagram account or Bluesky account with credentials
POST/v1/channels/connectadminStart a one-tap connect; returns connect_url for the account owner
GET/v1/channels/connect/{id}KeyConnect status: pending, connected (with channel_id), failed, expired
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/keys/{id}/rotateKey / adminRotate a key (self = the calling key); the old key works through grace_seconds
GET/v1/onboardingKeyRemaining steps to go live and the next call
POST/v1/verify/email, /v1/verify/email/confirmadminEmail a 6-digit code, then submit it
POST/v1/verify/dns, /v1/verify/dns/checkadminGet a TXT record for your domain, then check it
POST/v1/verify/approvaladminEmail the owner a one-tap Approve link
GET/v1/verify/{id}KeyVerification status (poll this)
GET/v1/analyticsKeyDeliveries, failures, spend and pricing over time, per brand
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.

  • Same key, same body: the original result with "idempotent_replay": true and HTTP 200. No second post, no second charge.
  • Same key, different body: 409 idempotency_key_reused.

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 the time 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

Requests, signups and verification sends are rate limited. Exceeding a limit returns 429 rate_limited (or signup_limited) with a Retry-After header: wait that many seconds, then retry with the same Idempotency-Key.

Top up credits (MPP card or stablecoin, or 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 the payment and answers 200 with an X-PAYMENT-RESPONSE header. A payment that needs review returns 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 $20 credit",
    "mimeType": "application/json",
    "payTo": "0x...",
    "maxTimeoutSeconds": 300,
    "asset": "<USDC contract on Base>",
    "extra": { "name": "USD Coin", "version": "2" }
  }],
  "hint": "..."
}
{ "credited_usd": 20, "credit_usd": 20, "plan": "live", "transaction": "0x..." }

A settled transaction can credit only once. Top-ups need a verified owner (403 verification_required otherwise; see GET /v1/onboarding). The first top-up switches the account from sandbox to live.

MPP rails (coming soon, off during the beta). Send {"pack": "pack_20", "rail": "mpp_card"} (or "mpp_stablecoin"). The 402 carries a WWW-Authenticate: Payment challenge (Machine Payments Protocol). For a card, get a one-time Shared Payment Token for the network_id in the 402 body (for example from the Link agent wallet) for the exact amount; for a stablecoin, pay the Tempo or Solana challenge. Retry the same request with the credential in Payment-Authorization (your Bearer key stays in Authorization). Success returns 200 with a Payment-Receipt header; a payment that needs review returns 202 payment_under_review. Each payment credits once. A refund or chargeback removes the credits it bought, and top-ups pause while a dispute is open. MCP: get_topup_link.

Card checkout for a person (coming soon). POST /v1/billing/checkout with {"pack": "pack_20"} (admin key, verified owner, optional Idempotency-Key) returns 201 with a Stripe-hosted checkout_url and session_id. Give the link to the person paying; credits are added only when Stripe confirms the payment, then show in GET /v1/me. Off during the beta: 503 checkout_not_enabled. MCP: create_checkout_link.

Referral credits (coming soon). GET /v1/referrals (verified owners) returns your code, share link and stats. New accounts pass "ref": "<code>" to POST /v1/signup; the first referral recorded is the one that counts. GET /v1/referrals/lookup?code= checks a code (no key, rate limited). Rewards are credits only, never cash. MCP: get_referral_code.