Account
Store-wide settings shared by preorders and back in stock: setup, sending, integrations, languages, markets, locations and plan usage.
BetaAPI v2 is in public beta: endpoints, fields and behaviour can change before general availability. See the changelog
Last updated
Account settings are shop-level singletons: no :id in any path. Each write is what a dashboard settings page saves, through the same code, so the API and the dashboard never disagree. Writes are deep partial: absent fields are unchanged. Validation errors are keyed "<section>.<path>", for example "markets.preorder_location_filter.shopify_location_ids".
Shopify ids are shopify_<thing>_id and always a gid (gid://shopify/Location/111). Writes also accept the numeric tail.
Path prefix: /account
Endpoints
Section titled “Endpoints”| Method | Path | What it is |
|---|---|---|
GET |
/account/setup |
Whether STOQ’s app embed is on, with the theme editor link when it is off |
GET |
/account/setup_guide |
The Home setup guide’s steps for back in stock and preorders |
POST |
/account/setup_guide/skip |
Skip the setup guide, as its Skip button does |
GET / PATCH |
/account/sending |
Who sends each notification, the email and SMS providers, sender, reply-to |
POST |
/account/sending/verify_domain |
Check the custom sending domain with Amazon SES |
GET / PATCH |
/account/templates/email_style |
Font, header and description text sizes, logo size and logo of every STOQ email |
POST / DELETE |
/account/templates/email_style/logo |
Upload or remove the email logo |
GET |
/account/integrations |
Every integration’s status, setup steps and links (?category=, ?status=) |
GET |
/account/integrations/{id} |
One integration’s status |
PATCH |
/account/integrations |
Customer sync, Omnisend / Mailchimp keys, Meta pixel, Flow triggers, Postscript keyword |
GET |
/account/integrations/mailchimp/audiences |
Audiences for mailchimp.list_id |
GET |
/account/integrations/postscript/keywords |
Keywords for postscript.keyword |
POST |
/account/integrations/{id}/connect |
Connect: the dashboard link for OAuth, or an API key (api_key, list_id, reconnect) |
POST |
/account/integrations/{provider}/disconnect |
Disconnect any integration: revoke OAuth, remove a key, or turn a switch off |
GET |
/account/integrations/{id}/events |
Connects, disconnects, key and settings changes (limit, before) |
POST |
/account/integrations/{channel}/flows/{verify,send_test_event,auto_setup,confirm,reset} |
Klaviyo / Postscript flow setup |
GET / PATCH |
/account/languages |
Dashboard language, multi-language, translated product titles |
POST |
/account/languages/reset_locale |
Reset one language’s copy to STOQ’s default text |
GET / PATCH |
/account/markets |
Market setup, market-to-location links, market URLs, preorder location filter |
GET |
/account/locations |
Shopify locations |
GET |
/account/plan_usage |
Plan, the limits it includes, and a month’s usage |
Sending
Section titled “Sending”sms_provider is stoq (STOQ sends), klaviyo or postscript; email_provider is stoq, klaviyo or flow.
STOQ sets three read-only flags, and a PATCH carrying any of them is refused: email_allowed (false when STOQ has stopped the shop’s email), sms_allowed (SMS is approved) and sms_restricted_content_verified (STOQ has reviewed the shop’s SMS content).
custom_address_status says where sending from the merchant’s own address stands:
not_set:use_custom_email_addressis off, oremail_reply_to_addressis empty or STOQ’s own address.pending: the toggle is on with the merchant’s address, but DKIM for its domain is not verified yet (email_dkim_verifiedis false). Mail goes from STOQ’s address (restock@<email_subdomain>.stoqapp.com, orpreorders@for preorder emails) until it is.verified: DKIM is verified; mail goes from the merchant’s address (email_from_address).
It is read from the stored email_dkim_verified, which STOQ refreshes from Amazon SES when the address changes and on a regular check, so a DNS change can take a while to show. The DNS records to add while pending are not on this read: POST /account/sending/verify_domain asks SES for them and returns them as dns_records (name, type, value), the same records the dashboard’s Address drawer gives with Download records. It also refreshes email_dkim_verified. email_subdomain is read only.
notifications nests each channel under its notification: back_in_stock has signup_thank_you (email, SMS), restock_alert and reminder (email, SMS); preorders has confirmation (email, SMS), shipping_update and payment_failed (email). Each channel is worked out by the same code that sends the notification:
sender: who sends it right now (stoq,klaviyo,postscriptorflow).nullmeans it is not sent and the customer gets nothing: SMS only falls back to STOQ when the shop is verified for restricted content, and a back-in-stock alert on Shopify Flow without its trigger is not sent.integration:{ id, href }of the chosen provider’s integration (Klaviyo, Postscript or Shopify Flow),nullfor STOQ’s own. Read it for the step that fixes a fallback.fallback_reason: why the sender is not the chosenemail_providerorsms_provider, elsenull.credentials_missing(Klaviyo or Postscript not connected),flow_unverified(that notification’s Klaviyo or Postscript flow is not verified),flow_trigger_disabled(the Shopify Flow trigger is off),flow_not_supported(Shopify Flow has no trigger for reminders or preorder emails),custom_html_template(a custom HTML email always goes through STOQ).
Shop with Klaviyo email, the restock alert flow verified and the reminder flow not (trimmed):
{ "email_provider": "klaviyo", "sms_provider": "stoq", "notifications": { "back_in_stock": { "restock_alert": { "email": { "sender": "klaviyo", "fallback_reason": null }, "sms": { "sender": "stoq", "fallback_reason": null } }, "reminder": { "email": { "sender": "stoq", "fallback_reason": "flow_unverified" } } }, "preorders": { "confirmation": { "email": { "sender": "stoq", "fallback_reason": "flow_unverified" } } } }}Integrations
Section titled “Integrations”GET /account/integrations returns integrations, one entry per integration: klaviyo, postscript, shopify_flow, judgeme, smile, gorgias, omnisend, mailchimp, meta_pixel, split_order, shopify_customers. Filter with category (marketing, shopify or other, the sections of the Integrations page) or status.
status:not_connected(nothing set up),off(set up but not in use),setup_incomplete(a required step is left),needs_attention(something that worked is broken, or it is the chosen provider but does not send),connected. Every status butconnectedhas areason{ code, message }.connection:method(oauth,api_keyorswitch),connected,legacy(a Klaviyo private key) andkey_hint(the last 4 characters of a saved key). Keys and tokens are never returned.selected_for: the channels it sends on, fromemail_providerandsms_provideron/account/sending.steps: what setup needs, each with astate(done,todo,optional,blocked) and, where there is one, theactionthat runs it.settings,plan(required:freeorpaid,available),actions, andlinksto the dashboard panel and the help article.
Klaviyo, Postscript and Shopify Flow are needs_attention when they are the chosen email_provider or sms_provider but a notification does not send through them: it goes out from STOQ instead (reason.code is the fallback_reason from /account/sending), it is not sent at all (notifications_blocked), or the integration isn’t connected (provider_not_connected). The Postscript keyword is optional. Klaviyo settings.flows is how the merchant chose to set up each campaign type’s flows, per channel: automatic (STOQ built them), manual (the merchant did), or null before they chose; set it with /account/integrations/{channel}/flows/auto_setup, confirm and reset.
{ "id": "klaviyo", "name": "Klaviyo", "category": "marketing", "status": "needs_attention", "reason": { "code": "flow_unverified", "message": "Reminder email goes out from STOQ, not Klaviyo: the flow is not verified." }, "connection": { "method": "oauth", "connected": true, "legacy": false, "key_hint": null }, "selected_for": ["email"], "plan": { "required": "paid", "available": true }, "steps": [ { "id": "connect", "state": "done", "required": true }, { "id": "email.verify_flow", "state": "todo", "required": true, "fallback": "stoq", "scope": { "channel": "email", "notifications": ["reminder"] }, "action": { "verb": "POST", "path": "/account/integrations/klaviyo_email/flows/verify" } } ], "settings": { "sync_customers": true, "profile_tag": "restock-rocket", "flows": { "restock": { "email": "automatic", "sms": null }, "preorder": { "email": "manual", "sms": null }, "wishlist_reminder": { "email": null, "sms": null }, "signup_thank_you": { "email": null, "sms": null } } }, "actions": ["disconnect", "update", "run_step"], "links": { "dashboard_url": "https://admin.shopify.com/store/acme/apps/restockrocket-1/integrations?platform=klaviyo", "help_url": "https://help.stoqapp.com/back-in-stock/connect-klaviyo-for-back-in-stock/" } }Each email and sms block of the back-in-stock and preorder template reads adds integration, the { id, href } of the chosen provider’s integration.
Connecting
Section titled “Connecting”POST /account/integrations/{id}/connect never connects an OAuth integration (Klaviyo, Judge.me, Smile.io, Gorgias) by itself: it returns 201 with connect_url, expires_at and single_use: true. The link opens the integration’s panel in the STOQ dashboard, where the merchant presses Connect and approves in the provider. Shopify asks for a staff login for that store first, so a leaked link can’t connect another store. Show the link to the merchant; don’t open or fetch it.
A link works once, for 15 minutes. A new one replaces the last, and each integration gets 5 an hour (then 429 rate_limited). For Gorgias, send account (the <account>.gorgias.com subdomain) to fill it in. An expired link still opens the panel, and Connect then works as it does in the dashboard. Read the result from connect on GET /account/integrations/{id}: pending, expires_at, last_result (connected, denied, failed, expired) and last_result_at. After a failed connect, reason.code is provider_error.
API-key integrations (Postscript, Omnisend, Mailchimp) take api_key. STOQ checks Postscript keys and Mailchimp keys with the provider before saving; a key the provider refuses is 422 with code: key_rejected and nothing is saved. An integration that is already connected is 409 already_connected unless you send reconnect: true. Switches (Shopify Flow, Meta Pixel, SplitOrder, Shopify customers) are 422 not_applicable: turn them on with PATCH /account/integrations. Paid integrations on the free plan are 403 plan_required.
Through the MCP server (ChatGPT, Claude) API keys are never accepted, so that no key passes through an assistant. The REST API takes them. Connect returns 201 with key_url, the dashboard panel where the merchant pastes the key. An api_key sent anyway, to connect, PATCH /account/integrations or PATCH /account/sending, is refused with code: use_key_link, path and key_url, and nothing is saved.
GET /account/integrations/{id}/events lists what happened, newest first: connect_link_created, connected, connect_denied, connect_failed, key_link_issued, key_set, key_rejected, key_cleared, settings_changed (field names only), disconnected and auto_disconnected (the provider revoked STOQ’s access). Each has actor_surface (dashboard, quinn, mcp, api, system); changes saved in the dashboard are recorded as well. Page with before: next_cursor. Keys are never recorded.
Email style
Section titled “Email style”/account/templates/email_style is the look every STOQ email shares, back-in-stock and preorder alike, as the template editor sets it: font_family (one of the editor’s fonts), header_font_size, description_font_size and logo_size (whole pixels above 0). One change restyles every template.
logo_url is the uploaded logo (null when there is none). Upload it with POST /account/templates/email_style/logo, sending the image as a multipart file field, as the template editor does. It must be a PNG, JPEG, GIF or WebP file of 2 MB or less, judged from the file’s bytes rather than the type it is sent as; anything else is a 422 and nothing is stored; DELETE removes it. Back-in-stock and preorder emails share the one logo. The MCP server can’t send files, so for them the logo is read-only.
curl -X POST https://app.stoqapp.com/api/v2/account/templates/email_style/logo \ -H "Authorization: Bearer $TOKEN" -F "file=@logo.png"Setup guide
Section titled “Setup guide”GET /account/setup_guide returns the Home setup guide for each product, back_in_stock and preorders:
{ "back_in_stock": { "chosen": true, "completed_at": null, "steps": [ { "step": "app_embed", "done": true }, { "step": "design_widget", "done": false }, { "step": "first_signup", "done": false }, { "step": "first_alert_sent", "done": false } ], "steps_done": 1, "next_step": "design_widget", "first_signup_id": null }, "preorders": { "chosen": false, "completed_at": null, "steps": [ { "step": "app_embed", "done": true }, { "step": "first_offer", "done": false }, { "step": "first_product", "done": false }, { "step": "first_order", "done": false } ], "steps_done": 1, "next_step": "first_offer" }}chosenis the product picked on the welcome screen. Preorder steps areapp_embed,first_offer,first_product,first_order.- A guide is complete once every step is done;
completed_atis set then, or when it is skipped. A skipped guide can’t be told apart from a finished one, as on Home. - Whether the app embed is on, and the link to turn it on, is
GET /account/setup. POST /account/setup_guide/skipskips both guides, or one withflow(back_in_stockorpreorders). It can’t be undone. Hiding the guide on Home (“Dismiss”) is kept in the browser only, so it isn’t in the API.
Markets
Section titled “Markets”{ "market_setup_type": "multiple_markets", "market_locations_enabled": true, "market_urls_enabled": false, "preorder_location_filter": { "enabled": false, "shopify_location_ids": [] }, "links": [ { "shopify_market_id": "gid://shopify/Market/12345", "shopify_location_id": "gid://shopify/Location/111" } ], "markets": [ { "shopify_market_id": "gid://shopify/Market/12345", "name": "United States", "status": "active", "countries": ["US"], "country_names": ["United States"] } ]}marketslists active markets; add?include_drafts=truefordraft_markets.linkscount only whilemarket_setup_typeismultiple_marketsandmarket_locations_enabledistrue.PATCHwithlinksorpreorder_location_filter.shopify_location_idsreplaces the stored list. Ids are checked against Shopify; if Shopify cannot be read the call returns503and saves nothing.- Switching
market_setup_typeturns the other setup’s switch off unless you send it. Switching tomultiple_marketsalso turns the back-in-stock location filter off, as the dashboard does. The response lists each inalso_changed.
Languages
Section titled “Languages”reset_locale takes an optional default_locale (first make it the language alerts and the widget use) and an optional locale (the language whose copy is reset). If locale is omitted, it is default_locale when that is sent, otherwise the shop’s default language. It overwrites the merchant’s copy in that language.
Plan usage
Section titled “Plan usage”Read-only. plan is the plan’s name and friendly_name; limits is what it includes (usage_type per_month pools email and SMS under notification_limit, per_channel uses email_limit and sms_limit). usage is the numbers the dashboard’s usage card shows; usage.alerts_over_limit is the alerts over the plan limit. Unlimited plans have none. ?period=YYYY-MM reads another month, metered against the current plan.
Developer settings
Section titled “Developer settings”Dev mode, custom CSS and custom JS are on /back_in_stock/settings/widget and /preorders/settings/developer.
Thanks for letting us know.
Still stuck? Ask Quinn or chat with support