Skip to main content
API REFERENCE

Webanto API reference

Call AI models, manage native catalog items, and automate connected-product enrichment and publication.

Authentication

Authenticate every request with a bearer API key in the Authorization header. Keys are secret — the raw value is shown only once, when you create or rotate a key.

Create and manage keys under Settings → API Keys.

Authorization: Bearer wba_live_xxxxxxxxxxxxxxxxxxxxxxxx

Base URL: https://api.webanto.com/api/v1

How a request flows

Every endpoint follows the same path — authenticate, check the key's scope, run inference, and return the result with its credit cost.

  1. Your server

    Send the request from your backend. API keys must never ship to a browser or client bundle.
  2. Bearer auth

    The gateway reads your key from the Authorization header and identifies your organization.
  3. Scope check

    The key's scopes (chat, embeddings, images) are matched against the endpoint before any work happens.
  4. Model inference

    The request runs on the model you named — qwen, nomic-embed-text, dinov3, qwen-image, or birefnet-general.
  5. Response + credits

    You get the JSON result (or PNG for remove-background) and the credit cost is deducted from your org balance.

Models

Five models across three scopes. Chat accepts optional images on the same endpoint; embeddings cover text and images; images are generated from text.

ModelEndpointKindScope
qwen/api/v1/chatChat — multimodal (text + vision)chat
nomic-embed-text/api/v1/embeddingsText embeddingsembeddings
clip/api/v1/embeddingsText + image embeddings — 512-d, shared space (cross-modal)embeddings
dinov3/api/v1/image-embeddingsImage embeddings — 1280-d, L2-normalizedembeddings
qwen-image/api/v1/imagesImage generation — Qwen-Image text-to-imageimages
birefnet-general/api/v1/images/remove-backgroundBackground removal — BiRefNet cutout, transparent PNG outimages

Commerce automation

Native catalog items are organization-owned and safe to upsert. Shopify and WooCommerce products remain provider-owned, so generated copy is proposed first and published only through an explicit, audited action.

EndpointScopePurpose
GET /catalog/itemscatalog:readList native products and services
PUT /catalog/itemscatalog:writeCreate or replace a native item by SKU
GET /productsproducts:readRead connected-store mirrors and proposed copy
POST /products/{id}/enrichproducts:enrichQueue AI-generated product copy
POST /products/{id}/publishproducts:publishPublish approved copy; send expectedVersion from the product's publicationVersion. Review in Webanto first. A changed version must be reviewed again.
GET /jobs/{id}jobs:readRead a job created by this API key
GET /orderscommerce:readList orders for the business, newest first
GET /customerscommerce:readList customers for the business
GET /social/postssocial:readList social posts with per-channel delivery state
GET /keywordsseo:readRead tracked keywords with a search window summary

Enrich and publish are asynchronous. Send a unique Idempotency-Key, then poll the returned job ID or include a public HTTPS webhook with a 32-character-or-longer signing secret. Routes whose scope is an OAuth scope also accept an OAuth access token as the bearer; inference, catalog and usage routes accept API keys only.

curl https://api.webanto.com/api/v1/products/product_id/enrich \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Idempotency-Key: 01J8WEBANTOEXAMPLE" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook": {
      "url": "https://example.com/webhooks/webanto",
      "secret": "replace-with-at-least-32-random-characters"
    }
  }'

Download the canonical OpenAPI 3.1 document

MCP server

Connect Claude, ChatGPT, Cursor or any Model Context Protocol client to your Webanto organization over streamable HTTP. Apps that support OAuth sign you in and ask which organization and scopes to grant; other clients send an API key header.

ToolScopePurpose
webanto_list_installed_appsintegrations:readInspect installed business apps and saved connection evidence
webanto_prepare_directory_handoffbacklinks:submissions:writePrepare exact public materials for a reviewed directory handoff
webanto_get_directory_handoffbacklinks:readInspect or recover an originating-key directory handoff
webanto_submit_directory_handoffbacklinks:submissions:writeIssue one natively approved directory browser handoff
webanto_resume_directory_handoffbacklinks:submissions:writeReconcile actual directory browser progress without claiming publication
webanto_get_internal_link_reportseo:readInspect saved customer internal-link health and orphan pages
webanto_list_internal_link_suggestionsseo:readInspect exact saved internal-link suggestions and source versions
webanto_prepare_internal_link_publicationcontent:publishStage one exact internal-link insertion for native publication review
webanto_list_contact_listscontacts:readDiscover saved contact lists and organization-owned membership counts
webanto_list_contact_tagscontacts:readDiscover exact active-contact tags for audience preview
webanto_prepare_social_publicationsocial:publishPrepare exact social text and connected accounts for native approval
webanto_get_social_publicationsocial:readInspect a frozen social plan and per-channel delivery acknowledgments
webanto_publish_social_postsocial:publishExecute an exact human-approved social publication
webanto_get_seo_auditseo:readAudit bounded saved published content with explicit coverage and omissions
webanto_list_social_postssocial:readRead saved social posts, schedules and native review links
webanto_get_social_postsocial:readInspect saved social copy and per-channel delivery outcomes
webanto_prepare_asset_publicationassets:publishFreeze exact image alt text and name for native review
webanto_get_asset_publicationassets:readInspect a saved image metadata plan and readback receipt
webanto_publish_assetassets:publishQueue the exact natively approved image metadata plan
webanto_verify_asset_publicationassets:publishRead back an uncertain image metadata attempt without sending it again
webanto_list_assetsassets:readRead bounded managed image metadata and native review links
webanto_get_assetassets:readReload an exact-version image source reference without granting external reuse
webanto_prepare_backlink_draftbacklinks:submissions:writePrepare an exact BetaList draft for native approval
webanto_get_backlink_draftbacklinks:readInspect an external draft and sanitized provider receipt
webanto_create_backlink_draftbacklinks:submissions:writeQueue the approved external draft without paying or publishing
webanto_refresh_backlink_draftbacklinks:submissions:writeRead provider draft status without repeating its creation
webanto_list_productsproducts:readSearch and filter connected-store products
webanto_get_productproducts:readRead one connected product with its proposed copy
webanto_list_copy_proposalsproducts:readRead the copy review queue and its counts
webanto_create_copy_proposalproducts:enrichQueue AI-generated product copy as a proposal
webanto_get_jobjobs:readRead a job created by this connection
webanto_list_search_console_opportunitiesseo:readRead Search Console page and product opportunities
webanto_search_memorymemory:readSearch the organization's knowledge base
webanto_list_backlink_opportunitiesbacklinks:readFind relevant backlink and listing opportunities and read the business tracker
webanto_get_backlink_submission_packbacklinks:readRead approved business materials and destination-specific submission guidance
webanto_update_backlink_trackingbacklinks:writeRecord submission outcomes in the business tracker; no external submission
webanto_get_contextCurrent organization read accessInspect the connected organization, scopes and supported versions
webanto_get_capabilitiesCurrent organization read accessInspect installed tools, current authority, costs and blockers
webanto_get_connection_healthCurrent organization read accessDiagnose connection authority without paid work or provider calls
webanto_get_workspace_profileCurrent organization read accessRead business identity and brand-profile source references
webanto_get_integration_healthproducts:readRead connected commerce accounts and stored sync observations
webanto_get_usageproducts:readInspect product-generation credit allowance and this key's cap
webanto_get_copy_proposalproducts:readInspect an existing proposal and its current version
webanto_update_copy_proposalproducts:proposals:writeEdit draft product copy with version checks; cannot approve or publish
webanto_get_backlink_profilebacklinks:readRead business-fit profile fields without private tracking notes
webanto_update_backlink_profilebacklinks:profile:writeEdit the business-fit profile; no external submission or public-disclosure approval
webanto_get_operationjobs:readInspect a generation job or draft-edit receipt created by this connection
webanto_get_operation_resultjobs:readRecover saved operation results under current read permissions
webanto_list_operationsjobs:readRecover one saved operation using its original retry key
webanto_prepare_product_publicationproducts:publishPrepare exact copy and store for native review
webanto_get_copy_proposal_reviewproducts:reviewInspect the trusted publication review
webanto_publish_productproducts:publishQueue the exact reviewed publication
webanto_verify_product_publicationproducts:publishRead back the saved publication attempt
webanto_prepare_backlink_submissionbacklinks:submissions:writePrepare an exact public-field browser handoff
webanto_get_backlink_attemptbacklinks:readInspect an attempt, client receipt and link evidence
webanto_submit_backlinkbacklinks:submissions:writeIssue a natively approved client-browser handoff
webanto_resume_backlink_attemptbacklinks:submissions:writeRecord progress or a challenge pause from the existing handoff
webanto_verify_backlinkbacklinks:submissions:writeQueue an independent public link observation
webanto_get_backlink_reportbacklinks:readRead attributed submission and observation summaries for a date range
webanto_refresh_search_consoleseo:refreshQueue a recoverable refresh through the existing Search Console API owner
webanto_list_keywordsseo:readRead Search Console queries with position movement and opportunities
webanto_list_keyword_topicsseo:readList keyword topics
webanto_list_tracked_keywordsseo:readList tracked keyword observations
webanto_list_content_entriescontent:readList organization content entries
webanto_get_content_entrycontent:readRead exact content copy
webanto_prepare_content_publicationcontent:publishPrepare exact content publication
webanto_get_content_publicationcontent:readInspect saved content publication
webanto_publish_content_entrycontent:publishExecute approved exact content publication
webanto_verify_content_publicationcontent:publishRead back saved content publication
webanto_get_billing_profilebilling:readRead organization billing profile
webanto_list_contactscontacts:readList organization contacts
webanto_get_contact_audiencecontacts:readPreview current eligible audience
webanto_list_automationsautomations:readList organization automations
webanto_get_automationautomations:readInspect the current automation definition and native execution limits
webanto_list_automation_enrollmentsautomations:readRead version-pinned enrollment progress and next-run times
webanto_list_automation_deliveriesautomations:readInspect stored automation email delivery history without retrying
webanto_list_merchant_orderscommerce:readList merchant orders by source
webanto_list_merchant_customerscommerce:readList merchant customers by source
webanto_list_merchant_paymentscommerce:readList merchant payment records
webanto_list_support_ticketssupport:readList organization support tickets
webanto_get_support_ticketsupport:readRead a client-visible support thread
webanto_get_email_reportemail:readRead campaign outcomes and attribution
webanto_list_email_campaignsemail:readList bounded organization email campaign drafts and versions.
webanto_get_email_campaignemail:readRead exact campaign content, audience selection and current version.
webanto_prepare_email_campaign_sendemail:sendFreeze an opted-in audience, message, sender and schedule for human review.
webanto_get_email_send_runemail:readInspect a frozen email plan, native review link and actual delivery outcomes.
webanto_send_email_campaignemail:sendExecute an exact human-reviewed email send through the existing sender.
webanto_get_mcp_supportCurrent organization read accessRead public MCP protocol support routes without sending caller data

Authenticate with a Webanto API key. Each tool needs its key scope, and the key owner must still hold the matching organization permission. The tool list shows only the tools the key can call, and no tool publishes to a live store.

{
  "mcpServers": {
    "webanto": {
      "type": "http",
      "url": "https://webanto.com/api/mcp",
      "headers": { "Authorization": "Bearer ${WEBANTO_API_KEY}" }
    }
  }
}

Setup guide for Claude, ChatGPT, Claude Code, Codex, Cursor and VS Code

Text embeddings

Generate text embeddings with nomic-embed-text or clip. Requires a key scoped for embeddings.

POST /api/v1/embeddings

model
The embeddings model to use: "nomic-embed-text" or "clip".
input
A single string or an array of up to 100 strings to embed.

Request

curl https://api.webanto.com/api/v1/embeddings \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nomic-embed-text",
    "input": ["a photo of a cat", "a photo of a dog"]
  }'

Response

{
  "model": "nomic-embed-text",
  "data": [
    { "index": 0, "embedding": [0.0123, -0.0456, "..."] },
    { "index": 1, "embedding": [0.0789, -0.0012, "..."] }
  ],
  "usage": { "total_tokens": 14 },
  "credits": 1
}

Image embeddings

Embed images with DINOv3 (1280-dimensional, L2-normalized). Send urls or images (not both), up to 60 per batch; URLs must be https. Requires a key scoped for embeddings.

POST /api/v1/image-embeddings

urls
An array of up to 60 https image URLs to embed. Provide urls or images, not both.
images
An array of up to 60 base64-encoded image objects with a b64 field. Provide images or urls, not both.

Request

curl https://api.webanto.com/api/v1/image-embeddings \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "urls": ["https://cdn.example.com/card.jpg"] }'

Response

{
  "model": "dinov3",
  "dim": 1280,
  "embeddings": [[0.009, -0.002, "..."]],
  "errors": [],
  "credits": 1
}

Image generation

Generate one image from a text prompt with Qwen-Image on dedicated GPU hardware. Returns a base64 PNG in about a minute; a busy GPU answers 503 with Retry-After. Requires a key scoped for images.

POST /api/v1/images

prompt
The text description of the image to generate (required, up to 4000 characters).
size
One of 1328x1328, 1664x928, 928x1664, 1472x1104, 1104x1472, 1584x1056, or 1056x1584. Defaults to 1328x1328. Credits scale with pixel count.
seed
Optional seed, 0 to 4294967295. The same prompt, size, and seed reproduce the image; the response returns the seed.

Request

curl https://api.webanto.com/api/v1/images \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "model": "qwen-image", "prompt": "a red maple leaf on a white background, studio product photo", "size": "1328x1328" }'

Response

{
  "created": 1730000000,
  "model": "qwen-image",
  "data": [{ "b64_json": "iVBORw0KGgo...", "seed": 1234567 }],
  "credits": 8
}

Background removal

Strips the background from a product photo and returns a transparent PNG. Unlike the other endpoints this one is binary in, binary out — POST the raw image bytes with an image/* Content-Type and the cutout comes back as the response body. Credits are reported in the X-Webanto-Credits header.

POST /api/v1/images/remove-background

Request

curl https://api.webanto.com/api/v1/images/remove-background \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @product.jpg \
  --output cutout.png

Response

HTTP/1.1 200 OK
Content-Type: image/png
X-Webanto-Model: birefnet-general
X-Webanto-Credits: 2

<raw PNG bytes — product on solid white>

Chat

Generate a chat completion. Requires a key scoped for chat.

POST /api/v1/chat

model
The chat model to use: "qwen".
messages
An array of up to 50 message objects with role and content fields. Role is system, user, or assistant.
max_tokens
Optional. Maximum tokens to generate (up to 4096). Reasoning calls are floored to 3072 so hidden reasoning can't consume the whole budget and return empty content; the floor is not applied when reasoning_effort is "none".
temperature
Optional. Sampling temperature between 0 and 2.
reasoning_effort
Optional; reasoning is on by default. Set to none for fewer completion tokens and credits on extraction or classification, with weaker multi-step reasoning. The SDK values low, medium, and high all keep reasoning on; they do not select different levels.
web_search
Optional; default false. Searches and reads pages before answering, reporting sources under x_webanto_web_search. Requires the search scope and cannot be combined with images. Uses more tokens than a direct call; enable when current information is needed.
X-Webanto-Priority
Optional header; default interactive. Use background for scheduled or batch jobs, which queue separately from capacity reserved for live users. Other values return 400.

Request

curl https://api.webanto.com/api/v1/chat \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen",
    "messages": [{ "role": "user", "content": "Explain embeddings in one sentence." }]
  }'

Response

{
  "id": "chatcmpl-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "object": "chat.completion",
  "created": 1721260800,
  "model": "qwen",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "..." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 12, "completion_tokens": 24, "total_tokens": 36 },
  "credits": 1
}

Request — reasoning disabled

For extraction, classification, and structured-output work, set reasoning_effort to "none". The model skips its hidden reasoning phase, which cuts completion tokens — and therefore credits — by a large factor, and removes the risk of an empty response caused by reasoning consuming the whole max_tokens budget.

curl https://api.webanto.com/api/v1/chat \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen",
    "reasoning_effort": "none",
    "messages": [{ "role": "user", "content": "Extract the year as a bare number: The treaty was signed in 1885." }]
  }'

Request — web search

Grounded answers cite what they read. The sources list is the loop’s own record of pages whose text entered the context, not the model’s claim, and sourced vs expected lets you see when an answer that should have been researched was not.

curl https://api.webanto.com/api/v1/chat \
  -H "Authorization: Bearer $WEBANTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen",
    "web_search": true,
    "messages": [{ "role": "user", "content": "What changed in the latest Next.js release?" }]
  }'
{
  "choices": [ { "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" } ],
  "usage": { "prompt_tokens": 4820, "completion_tokens": 310, "total_tokens": 5130 },
  "credits": 3,
  "x_webanto_web_search": {
    "sources": ["https://nextjs.org/blog/..."],
    "sourced": true,
    "expected": true,
    "searches": 1,
    "turns": 3,
    "exhausted": false
  }
}

Status & health

Two lightweight checks: an unauthenticated liveness probe and an authenticated key check.

GET /api/v1/status

Liveness — no auth. Returns status and timestamp fields.

{ "status": "ok", "timestamp": "..." }

GET /api/v1/health

Verifies your key and returns ok, organizationId, and scopes fields.

{ "ok": true, "organizationId": "...", "scopes": ["chat", "embeddings", "images"] }

Credits & rate limits

Embeddings are charged by number of inputs; chat is charged by total tokens used. Each response includes the credits charged. Requests are rate limited per API key; exceeding the limit returns 429 with a Retry-After header.

Background tasks and queues

Imports, product enrichment, and publication can run as saved background tasks. A task being accepted means it has been queued; its progress and results tell you when the work has finished.

  1. Queued

    Your task is saved and waiting to start or resume.
  2. Running

    Work is underway. Counts update as items finish.
  3. Check the results

    Review successful, failed, and unfinished items before taking the next action.

Why a task may wait

Tasks may wait for their scheduled turn, another step, or available AI capacity. Capacity waits resume automatically. Interactive requests have reserved capacity alongside background work; wait times vary with demand and task size. There is no fixed completion time.

Leaving the page or stopping work

Once a background task is accepted, closing the page does not cancel it. Check the latest product or task status when you return. If a task offers a cancel action, use that control; work already completed is not undone.

Retries and partial results

Waiting for AI capacity is not a failed attempt. Temporary task failures can be retried automatically within limits. If work fails or completes with issues, read the reported error and check which items succeeded before retrying. Repeatedly submitting the whole batch can start unnecessary work.

Generated does not always mean published

Generated copy may need quality checks or your approval before publication, depending on your settings. Publication can be a separate queued task. Check the publication result before assuming your connected store has changed.

Saved jobs through the API

Product enrichment and publication return HTTP 202 with a jobId. Keep that ID and poll GET /api/v1/jobs/{id}, or use the completion webhook you supplied. A running job can be waiting between steps. Reuse the same Idempotency-Key and request body when retrying the same submission.

Direct AI requests

Chat and embedding requests return their result within the HTTP call; a busy response does not create a saved background job for you. Batch clients should queue their own work, send X-Webanto-Priority: background on chat requests, and honor Retry-After on 429, 502, 503, and 504. Bound retries by attempts and an overall deadline, reuse the same Idempotency-Key, and surface a failure when that budget is exhausted.

Developer app webhooks

Apps registered under Settings, Developer apps can receive signed events from Webanto. Events only ever concern businesses that have connected your app, and only within the permissions that business granted.

Delivery and retries

Webanto sends each event as an HTTPS POST and waits up to 10 seconds. Any 2xx response counts as delivered. Anything else is retried up to 7 attempts in total, waiting about 1, 4, 16, 64, 256, 720 minutes between attempts (with a little random jitter). After the last attempt the delivery is marked failed; you can redeliver it from the delivery log.

Handling duplicates

Delivery is at least once. Every event has a stable id in the payload and in the delivery header, so treat an id you have already processed as a no-op. Redelivering from the log reuses the same event id.

Turning a webhook off

Redirects are not followed. If your endpoint answers 410 Gone, Webanto stops sending: the webhook is disabled and queued events are dropped. Save the webhook URL again in Developer apps to turn it back on.

Request headers

HeaderMeaning
Webanto-SignatureSignature in the form t=UNIX_SECONDS,v1=HEX_HMAC_SHA256. The signed message is the timestamp, a dot, then the raw request body, keyed with your webhook signing secret.
Webanto-EventThe event type, for example products.sync_completed.
Webanto-DeliveryA unique id for this delivery record. Use the id in the payload to deduplicate events.

Verify the signature

Compute the HMAC over the raw body exactly as received, compare in constant time, and reject timestamps more than 300 seconds old to block replays. Rotating the signing secret takes effect for every attempt that has not yet been sent.

const crypto = require("node:crypto");

// `rawBody` must be the exact bytes Webanto sent, before any JSON parsing.
function verifyWebhook(rawBody, headers, secret) {
  const header = headers["webanto-signature"] ?? "";
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const timestamp = Number(parts.t);
  const signature = parts.v1;
  if (!Number.isInteger(timestamp) || !signature) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Payload shape

Every event uses the same envelope. The data object depends on the event type.

{
  "id": "products.sync_completed:wfjob_123",
  "type": "products.sync_completed",
  "created_at": "2026-11-05T12:00:00.000Z",
  "organization_id": "org_123",
  "data": { "integration": "shopify", "mode": "delta", "total_synced": 12 }
}

Events

TypeSent when
app.installedA business installs your app (its first active connection).
app.uninstalledA business removes your app, or you archive it.
products.sync_completedA product sync finishes for a business that connected your app with product read access.
products.sync_failedA product sync fails after all retries for a business that connected your app with product read access.
product.publishedReviewed product copy is published for a business that connected your app with product read access.
social.post_publishedA social post is delivered to a channel for a business that connected your app with social read access.
social.post_failedA social post delivery fails for good for a business that connected your app with social read access.
order.createdA Webanto checkout order is created for a business that connected your app with commerce read access.
webhook.testYou choose Send test event in Developer apps. Always allowed; never sent on its own.

Developer app publish targets

An app that requests the social:publish permission and sets a publish endpoint appears in the post composer for every business that has connected it. Webanto sends the post to your endpoint and tracks the result like any other platform.

Setting up

Add an https address under Settings, Developer apps, Publish endpoint, and request the social:publish permission. The endpoint is signed with your webhook secret. Changing the endpoint sends the app back for review. A business must have connected the app with that permission before it can pick it.

The request

Webanto sends an HTTPS POST and waits up to 10 seconds. Verify the Webanto-Signature header exactly as for webhooks. The Webanto-Event header is social.publish, and Webanto-Delivery is a stable id for this post, so treat a repeat as a no-op.

Your response

Return 2xx with JSON containing externalId (and optionally url) once the post is published. Any other 4xx fails the post for good, and 410 Gone also turns the endpoint off until you save it again. A 5xx, 408, 429, a timeout or a network error is retried.

Request body

{
  "organizationId": "org_123",
  "postId": "post_123",
  "deliveryId": "delivery_123",
  "content": "New arrivals are in the shop.",
  "mediaUrls": ["https://cdn.example.com/media/new-arrivals.jpg"],
  "scheduledFor": null
}

Success response

{
  "externalId": "post-8841",
  "url": "https://app.example.com/posts/8841"
}

Developer app store connectors

An app that requests the products:read, products:publish permissions and sets a store connector endpoint can be connected by a business as its product store. Webanto syncs the catalog page by page, enriches it, and writes the improved copy back to your endpoint.

Setting up

Add an https address under Settings, Developer apps, Store connector endpoint, and request the products:read, products:publish permissions. Requests are signed with your webhook secret. Changing the endpoint sends the app back for review. A business installs the app, then connects it as a store.

Listing products

Webanto sends an HTTPS POST with the Webanto-Event header set to store.products.list and waits up to 10 seconds. Verify the Webanto-Signature header exactly as for webhooks. Return up to 100 products (Webanto asks for 50) and a nextCursor, or null on the last page. A modifiedAfter timestamp asks only for products changed since then.

Receiving updates

Webanto sends store.products.update with the externalId and a changes object holding only the fields it changed. Return 2xx with JSON containing the same externalId. Any other 4xx fails the push for good, and 410 Gone also turns the endpoint off until you save it again. A 5xx, 408, 429, a timeout or a network error is retried. A response that does not match the documented shape is rejected.

Reviewed description and SEO publication (v2)

Opt in by implementing store.products.copy.read.v2 and store.products.copy.publish.v2 on the same signed endpoint. The read event receives protocol_version 2 and the exact opaque externalId. Return a nonempty opaque revision, a required updatedAt matching the catalog observation, all three copy fields (null means absent), and last_operation. The existing API-key review page shows the exact destination, source revision and literal before/after copy for separate human approval. MCP callers must select developer_store_v2 when inspecting a custom-store proposal or preparing and inspecting its publication plan. Existing list and update events remain unchanged; an older connector is refused for reviewed publication.

Atomic application and readback

For store.products.copy.publish.v2, atomically compare expected_revision, update only the supplied copy fields and persist operation_id plus payload_hash with the resulting revision. Scope operation identity to organizationId. Hash the canonical sorted-key JSON object containing protocol_version, organizationId, app_id (your dev: app identifier), externalId, expected_revision, operation_id and changes using SHA-256. A repeated operation with identical material returns its durable acknowledgment; conflicting material or revision returns 409 without a write. Return outcome applied only after commit. Each later read must return the last applied operation binding with the current fields; any other product mutation clears that binding. Webanto independently checks that binding and every selected field. A timeout, malformed acknowledgment, 409 or other failed final response triggers readback only; Webanto never automatically repeats this reviewed write. A receipt proves the observed copy fields at that time, not storefront appearance, other fields or future consistency.

Reviewed copy read response (before any operation)

{
  "protocol_version": 2,
  "externalId": "sku-1001",
  "revision": "opaque-product-revision-7",
  "updatedAt": "2026-11-05T09:30:00Z",
  "fields": {
    "description": "<p>Heavy cotton canvas.</p>",
    "seo_title": "Canvas tote bag",
    "seo_description": null
  },
  "last_operation": null
}

Reviewed copy publish request

{
  "organizationId": "org_123",
  "protocol_version": 2,
  "externalId": "sku-1001",
  "expected_revision": "opaque-product-revision-7",
  "operation_id": "publication_plan_id",
  "payload_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "changes": {
    "description": "<p>The exact reviewed copy.</p>",
    "seo_title": "Reviewed tote title"
  }
}

Applied acknowledgment (readback is still required)

{
  "protocol_version": 2,
  "externalId": "sku-1001",
  "operation_id": "publication_plan_id",
  "payload_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "outcome": "applied"
}

List request body

{
  "organizationId": "org_123",
  "cursor": null,
  "modifiedAfter": null,
  "pageSize": 50
}

List response

{
  "products": [
    {
      "externalId": "sku-1001",
      "title": "Canvas tote bag",
      "description": "<p>Heavy cotton canvas.</p>",
      "sku": "TOTE-1001",
      "price": { "amountMinor": 2400, "currency": "USD" },
      "status": "active",
      "images": [{ "url": "https://cdn.example.com/tote.jpg", "alt": "Canvas tote bag" }],
      "productType": "simple",
      "updatedAt": "2026-11-05T09:30:00Z"
    }
  ],
  "nextCursor": "page-2"
}

Update request body

{
  "organizationId": "org_123",
  "externalId": "sku-1001",
  "changes": {
    "title": "Heavy canvas tote bag",
    "description": "<p>A roomy tote in heavy cotton canvas.</p>",
    "seoTitle": "Heavy canvas tote bag | Example Shop",
    "seoDescription": "A roomy cotton canvas tote for everyday carrying.",
    "tags": ["tote", "canvas"],
    "status": "active"
  }
}

Update response

{
  "externalId": "sku-1001"
}

Embedded settings pages

An app can ship its own settings screen. A business opens it inside Webanto in a sandboxed iframe, launched with a short-lived signed session that your app verifies.

Launch

Set a Settings page URL on your app (https, public host, never a Webanto address). Webanto POSTs the form field webanto_session to that URL from inside the iframe, so the token never appears in a URL or Referer. The token expires after 120 seconds: exchange it for your own session on the first load.

Verify the session

The token is signed with your app's webhook signing secret. Check the signature in constant time, then reject it if exp has passed or iat is more than 30 seconds ahead. Only organization managers with the integrations:update permission can open the page.

Talking to Webanto

The iframe is sandboxed (allow-scripts allow-forms allow-same-origin) and cannot navigate the parent page. Send webanto:resize with a height (clamped to 320 to 4000 pixels) to resize it, and webanto:toast with a message (up to 200 characters, shown as plain text) to show a notice. Every other message is ignored.

Token layout

v1.<base64url(JSON payload)>.<base64url(HMAC-SHA256)>

Verification example

const crypto = require("node:crypto");

// Read the token from the POST body field "webanto_session", never from the URL.
function verifyWebantoSession(token, secret) {
  const parts = String(token).split(".");
  if (parts.length !== 3 || parts[0] !== "v1") return null;
  const [version, payloadSegment, signature] = parts;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${version}.${payloadSegment}`)
    .digest();
  const actual = Buffer.from(signature, "base64url");
  if (expected.length !== actual.length || !crypto.timingSafeEqual(expected, actual)) return null;
  const payload = JSON.parse(Buffer.from(payloadSegment, "base64url").toString("utf8"));
  const now = Math.floor(Date.now() / 1000);
  if (payload.iat > now + 30 || payload.exp <= now) return null;
  return payload;
}

Payload

{
  "appId": "dev:acme-reports",
  "organizationId": "org_123",
  "userId": "user_456",
  "role": "ADMIN",
  "permissions": ["integrations:update"],
  "locale": "en",
  "nonce": "uQ3x9mY0n2sVd1kLw8Zp5A",
  "iat": 1793880000,
  "exp": 1793880120
}

Errors

400Invalid request — malformed JSON, missing fields, or an unknown model.
401Missing or invalid API key.
402Credit quota exceeded for the organization.
403The API key is not scoped for this endpoint.
429Rate limit exceeded — retry after the interval in the Retry-After header.
502The inference backend was unavailable. Honor Retry-After and retry within your retry budget using the same Idempotency-Key.
503AI capacity is busy. Queue the request on your side and honor Retry-After before retrying.
504The inference request timed out. Honor Retry-After and retry within your overall deadline.
Newsletter

Stay Ahead

Occasional, engineering-led notes on applied AI — what we're building, running, and learning in production.