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_xxxxxxxxxxxxxxxxxxxxxxxxBase 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.
Your server
Send the request from your backend. API keys must never ship to a browser or client bundle.Bearer auth
The gateway reads your key from the Authorization header and identifies your organization.Scope check
The key's scopes (chat, embeddings, images) are matched against the endpoint before any work happens.Model inference
The request runs on the model you named — qwen, nomic-embed-text, dinov3, qwen-image, or birefnet-general.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.
| Model | Endpoint | Kind | Scope |
|---|---|---|---|
| qwen | /api/v1/chat | Chat — multimodal (text + vision) | chat |
| nomic-embed-text | /api/v1/embeddings | Text embeddings | embeddings |
| clip | /api/v1/embeddings | Text + image embeddings — 512-d, shared space (cross-modal) | embeddings |
| dinov3 | /api/v1/image-embeddings | Image embeddings — 1280-d, L2-normalized | embeddings |
| qwen-image | /api/v1/images | Image generation — Qwen-Image text-to-image | images |
| birefnet-general | /api/v1/images/remove-background | Background removal — BiRefNet cutout, transparent PNG out | images |
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.
| Endpoint | Scope | Purpose |
|---|---|---|
| GET /catalog/items | catalog:read | List native products and services |
| PUT /catalog/items | catalog:write | Create or replace a native item by SKU |
| GET /products | products:read | Read connected-store mirrors and proposed copy |
| POST /products/{id}/enrich | products:enrich | Queue AI-generated product copy |
| POST /products/{id}/publish | products:publish | Publish approved copy; send expectedVersion from the product's publicationVersion. Review in Webanto first. A changed version must be reviewed again. |
| GET /jobs/{id} | jobs:read | Read a job created by this API key |
| GET /orders | commerce:read | List orders for the business, newest first |
| GET /customers | commerce:read | List customers for the business |
| GET /social/posts | social:read | List social posts with per-channel delivery state |
| GET /keywords | seo:read | Read 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"
}
}'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.
| Tool | Scope | Purpose |
|---|---|---|
| webanto_list_installed_apps | integrations:read | Inspect installed business apps and saved connection evidence |
| webanto_prepare_directory_handoff | backlinks:submissions:write | Prepare exact public materials for a reviewed directory handoff |
| webanto_get_directory_handoff | backlinks:read | Inspect or recover an originating-key directory handoff |
| webanto_submit_directory_handoff | backlinks:submissions:write | Issue one natively approved directory browser handoff |
| webanto_resume_directory_handoff | backlinks:submissions:write | Reconcile actual directory browser progress without claiming publication |
| webanto_get_internal_link_report | seo:read | Inspect saved customer internal-link health and orphan pages |
| webanto_list_internal_link_suggestions | seo:read | Inspect exact saved internal-link suggestions and source versions |
| webanto_prepare_internal_link_publication | content:publish | Stage one exact internal-link insertion for native publication review |
| webanto_list_contact_lists | contacts:read | Discover saved contact lists and organization-owned membership counts |
| webanto_list_contact_tags | contacts:read | Discover exact active-contact tags for audience preview |
| webanto_prepare_social_publication | social:publish | Prepare exact social text and connected accounts for native approval |
| webanto_get_social_publication | social:read | Inspect a frozen social plan and per-channel delivery acknowledgments |
| webanto_publish_social_post | social:publish | Execute an exact human-approved social publication |
| webanto_get_seo_audit | seo:read | Audit bounded saved published content with explicit coverage and omissions |
| webanto_list_social_posts | social:read | Read saved social posts, schedules and native review links |
| webanto_get_social_post | social:read | Inspect saved social copy and per-channel delivery outcomes |
| webanto_prepare_asset_publication | assets:publish | Freeze exact image alt text and name for native review |
| webanto_get_asset_publication | assets:read | Inspect a saved image metadata plan and readback receipt |
| webanto_publish_asset | assets:publish | Queue the exact natively approved image metadata plan |
| webanto_verify_asset_publication | assets:publish | Read back an uncertain image metadata attempt without sending it again |
| webanto_list_assets | assets:read | Read bounded managed image metadata and native review links |
| webanto_get_asset | assets:read | Reload an exact-version image source reference without granting external reuse |
| webanto_prepare_backlink_draft | backlinks:submissions:write | Prepare an exact BetaList draft for native approval |
| webanto_get_backlink_draft | backlinks:read | Inspect an external draft and sanitized provider receipt |
| webanto_create_backlink_draft | backlinks:submissions:write | Queue the approved external draft without paying or publishing |
| webanto_refresh_backlink_draft | backlinks:submissions:write | Read provider draft status without repeating its creation |
| webanto_list_products | products:read | Search and filter connected-store products |
| webanto_get_product | products:read | Read one connected product with its proposed copy |
| webanto_list_copy_proposals | products:read | Read the copy review queue and its counts |
| webanto_create_copy_proposal | products:enrich | Queue AI-generated product copy as a proposal |
| webanto_get_job | jobs:read | Read a job created by this connection |
| webanto_list_search_console_opportunities | seo:read | Read Search Console page and product opportunities |
| webanto_search_memory | memory:read | Search the organization's knowledge base |
| webanto_list_backlink_opportunities | backlinks:read | Find relevant backlink and listing opportunities and read the business tracker |
| webanto_get_backlink_submission_pack | backlinks:read | Read approved business materials and destination-specific submission guidance |
| webanto_update_backlink_tracking | backlinks:write | Record submission outcomes in the business tracker; no external submission |
| webanto_get_context | Current organization read access | Inspect the connected organization, scopes and supported versions |
| webanto_get_capabilities | Current organization read access | Inspect installed tools, current authority, costs and blockers |
| webanto_get_connection_health | Current organization read access | Diagnose connection authority without paid work or provider calls |
| webanto_get_workspace_profile | Current organization read access | Read business identity and brand-profile source references |
| webanto_get_integration_health | products:read | Read connected commerce accounts and stored sync observations |
| webanto_get_usage | products:read | Inspect product-generation credit allowance and this key's cap |
| webanto_get_copy_proposal | products:read | Inspect an existing proposal and its current version |
| webanto_update_copy_proposal | products:proposals:write | Edit draft product copy with version checks; cannot approve or publish |
| webanto_get_backlink_profile | backlinks:read | Read business-fit profile fields without private tracking notes |
| webanto_update_backlink_profile | backlinks:profile:write | Edit the business-fit profile; no external submission or public-disclosure approval |
| webanto_get_operation | jobs:read | Inspect a generation job or draft-edit receipt created by this connection |
| webanto_get_operation_result | jobs:read | Recover saved operation results under current read permissions |
| webanto_list_operations | jobs:read | Recover one saved operation using its original retry key |
| webanto_prepare_product_publication | products:publish | Prepare exact copy and store for native review |
| webanto_get_copy_proposal_review | products:review | Inspect the trusted publication review |
| webanto_publish_product | products:publish | Queue the exact reviewed publication |
| webanto_verify_product_publication | products:publish | Read back the saved publication attempt |
| webanto_prepare_backlink_submission | backlinks:submissions:write | Prepare an exact public-field browser handoff |
| webanto_get_backlink_attempt | backlinks:read | Inspect an attempt, client receipt and link evidence |
| webanto_submit_backlink | backlinks:submissions:write | Issue a natively approved client-browser handoff |
| webanto_resume_backlink_attempt | backlinks:submissions:write | Record progress or a challenge pause from the existing handoff |
| webanto_verify_backlink | backlinks:submissions:write | Queue an independent public link observation |
| webanto_get_backlink_report | backlinks:read | Read attributed submission and observation summaries for a date range |
| webanto_refresh_search_console | seo:refresh | Queue a recoverable refresh through the existing Search Console API owner |
| webanto_list_keywords | seo:read | Read Search Console queries with position movement and opportunities |
| webanto_list_keyword_topics | seo:read | List keyword topics |
| webanto_list_tracked_keywords | seo:read | List tracked keyword observations |
| webanto_list_content_entries | content:read | List organization content entries |
| webanto_get_content_entry | content:read | Read exact content copy |
| webanto_prepare_content_publication | content:publish | Prepare exact content publication |
| webanto_get_content_publication | content:read | Inspect saved content publication |
| webanto_publish_content_entry | content:publish | Execute approved exact content publication |
| webanto_verify_content_publication | content:publish | Read back saved content publication |
| webanto_get_billing_profile | billing:read | Read organization billing profile |
| webanto_list_contacts | contacts:read | List organization contacts |
| webanto_get_contact_audience | contacts:read | Preview current eligible audience |
| webanto_list_automations | automations:read | List organization automations |
| webanto_get_automation | automations:read | Inspect the current automation definition and native execution limits |
| webanto_list_automation_enrollments | automations:read | Read version-pinned enrollment progress and next-run times |
| webanto_list_automation_deliveries | automations:read | Inspect stored automation email delivery history without retrying |
| webanto_list_merchant_orders | commerce:read | List merchant orders by source |
| webanto_list_merchant_customers | commerce:read | List merchant customers by source |
| webanto_list_merchant_payments | commerce:read | List merchant payment records |
| webanto_list_support_tickets | support:read | List organization support tickets |
| webanto_get_support_ticket | support:read | Read a client-visible support thread |
| webanto_get_email_report | email:read | Read campaign outcomes and attribution |
| webanto_list_email_campaigns | email:read | List bounded organization email campaign drafts and versions. |
| webanto_get_email_campaign | email:read | Read exact campaign content, audience selection and current version. |
| webanto_prepare_email_campaign_send | email:send | Freeze an opted-in audience, message, sender and schedule for human review. |
| webanto_get_email_send_run | email:read | Inspect a frozen email plan, native review link and actual delivery outcomes. |
| webanto_send_email_campaign | email:send | Execute an exact human-reviewed email send through the existing sender. |
| webanto_get_mcp_support | Current organization read access | Read 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.pngResponse
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.
Queued
Your task is saved and waiting to start or resume.Running
Work is underway. Counts update as items finish.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
| Header | Meaning |
Webanto-Signature | Signature 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-Event | The event type, for example products.sync_completed. |
Webanto-Delivery | A 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
| Type | Sent when |
app.installed | A business installs your app (its first active connection). |
app.uninstalled | A business removes your app, or you archive it. |
products.sync_completed | A product sync finishes for a business that connected your app with product read access. |
products.sync_failed | A product sync fails after all retries for a business that connected your app with product read access. |
product.published | Reviewed product copy is published for a business that connected your app with product read access. |
social.post_published | A social post is delivered to a channel for a business that connected your app with social read access. |
social.post_failed | A social post delivery fails for good for a business that connected your app with social read access. |
order.created | A Webanto checkout order is created for a business that connected your app with commerce read access. |
webhook.test | You 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
}
