Overview
Self-hosted, multi-tenant bridge that turns a customer lead (name, address, cart)
into a Shopify draft order formatted as an anonymous invoice. The operator registers
Shopify stores once via OAuth; any landing page then integrates by POSTing one JSON
request to /api/checkout and redirecting the buyer to the
returned invoice_url.
There's no quota, no billing, no SDK — just a tiny HTTP API. The whole integration fits in one function, see Quick integration.
Authentication
Required on POST /api/checkout. Shared secret. Value lives in the bridge's .env as BRIDGE_API_KEY. Get it from the operator. Never send this from browser JS — call the bridge from your own backend.
curl -H "X-Bridge-Key: <your key>" ...
Required on every /api/admin/* call. Obtain by POSTing the admin password to /api/admin/login. JWT is valid for 24 hours.
curl -H "Authorization: Bearer <jwt>" ...
Quick integration (what most agents need)
This is the entire landing-page integration. Call it from your server-side controller (Node/PHP/Python/Go — doesn't matter). The request body is stable, the response is a single URL.
// Node (server-side)
const res = await fetch("https://shopify.anonbusiness.com/api/checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Bridge-Key": process.env.BRIDGE_API_KEY,
},
body: JSON.stringify({
store_id: 1,
customer: {
firstName: "Mohamed",
lastName: "Alaoui",
email: "m.alaoui@example.com",
address: "12 Rue des Orangers",
city: "Casablanca",
zip: "20000",
},
items: [
{ price: 219, quantity: 1 },
],
}),
});
const { invoice_url } = await res.json();
// redirect the buyer:
response.redirect(invoice_url);
price you send is forwarded to Shopify as-is — the bridge does not validate it against a catalog. Compute the price server-side from the product ID / pack size, never from hidden form fields the browser could edit.
title fields you send are ignored. The bridge overwrites them with "Invoice #NNNN" before the request reaches Shopify, so the resulting draft order reads as an invoice rather than named products. Your own price and quantity are preserved.
Checkout
Creates a Shopify draft order for a connected store and returns its invoice_url. The primary integration endpoint for landing pages.
Body
| Field | Type | Notes |
|---|---|---|
req store_id | integer | Bridge's internal store ID (list via /api/admin/stores). Store must have status = "connected". |
req customer | object | See fields below. All required. |
customer.firstName | string | |
customer.lastName | string | |
customer.email | string | Email format. |
customer.address | string | Street line. |
customer.city | string | |
customer.zip | string | |
req items | array<object> | At least 1 item. |
items[].price | number | Unit price, store currency. |
items[].quantity | integer ≥ 1 |
Response 200
{ "invoice_url": "https://my-shop.myshopify.com/12345/invoices/abcdef" }
Error responses
| Code | When |
|---|---|
| 401 | Missing or wrong X-Bridge-Key |
| 404 | store_id doesn't exist, or store status is still pending (OAuth not completed) |
| 500 | Shopify API returned an error — body includes { "error": ... } |
curl
curl -X POST https://shopify.anonbusiness.com/api/checkout \
-H "Content-Type: application/json" \
-H "X-Bridge-Key: $BRIDGE_API_KEY" \
-d '{
"store_id": 1,
"customer": { "firstName":"Mohamed","lastName":"Alaoui","email":"a@b.com","address":"12 Rue","city":"Casablanca","zip":"20000" },
"items": [{ "price": 219, "quantity": 1 }]
}'
Admin
Exchanges the admin password for a Bearer JWT. JWT is valid for 24 hours.
Body
{ "password": "your-admin-password" }
Response 200
{ "token": "eyJhbGciOiJIUzI1NiIs..." }
Lists every registered store. access_token is included for connected stores — treat the response as sensitive.
Response 200
[
{
"id": 1,
"name": "My Morocco Store",
"domain": "my-shop.myshopify.com",
"client_id": "abc123...",
"access_token": "shpat_...",
"status": "connected",
"created_at": "2026-04-22T00:14:00Z"
}
]
Registers a new Shopify store with status pending. Run /api/auth/shopify/{id} afterwards to complete OAuth and flip the status to connected.
Body
{
"name": "My Morocco Store",
"domain": "my-shop.myshopify.com",
"client_id": "abc123...",
"client_secret": "shpss_..."
}
Response 200
{ "id": 1, "message": "Store added successfully" }
Removes a store and its stored access token. Does not revoke the token inside Shopify — do that separately from the Shopify admin.
Response 200
{ "message": "Store deleted" }
OAuth
Starts the Shopify install flow. Returns a 302 to Shopify's authorize URL. After the merchant approves, Shopify sends them to the callback below.
Scopes requested: write_draft_orders, read_customers, read_products.
Called by Shopify, not by you. Verifies the HMAC signature, cross-checks the shop parameter against the registered store domain, exchanges the code for an access token, and marks the store as connected.
Health
Zero-auth liveness probe. Use in uptime monitors.
Response 200
{ "ok": true, "service": "shopify-bridge" }
Machine-readable
Point your AI agent / codegen tool at these URLs rather than this HTML — they're tighter and more reliable to parse.
/openapi.json— OpenAPI 3.1 spec. Every endpoint, every schema, every status code./llms.txt— short plaintext pointer file following the llms.txt convention. Links to the spec + this page.