← Back to docs

Public API

REST endpoints to create storefronts, manage products, read orders, and run AI generation jobs from your own code or scripts. Production base URL: https://api.instxnt.xyz.

Authentication

All requests require a session cookie (set after login) plus a short-lived CSRF token in the X-CSRF-Token header for any mutating request (POST, PATCH, DELETE).

1. Get a CSRF token

curl -s https://api.instxnt.xyz/api/auth/csrf \
  -H "Cookie: $YOUR_SESSION_COOKIE"
# → { "token": "csrf_..." }

2. Use it on mutating requests

curl -X POST https://api.instxnt.xyz/api/stores \
  -H "Content-Type: application/json" \
  -H "Cookie: $YOUR_SESSION_COOKIE" \
  -H "X-CSRF-Token: csrf_..." \
  -d '{"name":"Test Store","slug":"test-store"}'

Sessions are tied to your account. For programmatic access, log in once via the dashboard, copy the session cookie out of your browser's devtools, and paste into your script. Long-lived API tokens for headless automation are available on Pro — contact support@instxnt.xyz to request one.

Stores

A "store" is a single-product storefront. Each owner can have unlimited stores on every plan.

List your stores

GET /api/stores
# → { "stores": [ { "id": "st_...", "name": "...", "subdomain": "...", "status": "live" }, ... ] }

Create a store

POST /api/stores
{
  "name": "Magnetic phone mount",
  "subdomain": "phone-mount-test"   // optional; auto-generated if omitted
}
# → 201 { "id": "st_abc123", "subdomain": "phone-mount-test", "url": "https://phone-mount-test.instxnt.store", "status": "draft" }

Update store metadata

PATCH /api/stores/st_abc123
{
  "settings": {
    "theme": "neon",
    "primary_color": "#0066ff",
    "ad_pixels": { "meta": "1234567890", "tiktok": "C7..." }
  }
}

Delete a store (soft delete)

DELETE /api/stores/st_abc123
# → 204 No Content

Soft-deleted stores are hidden but recoverable for 30 days. After 30 days they're hard-deleted along with their R2 image objects.

Products

Each storefront has one primary product. Products carry pricing, variants, descriptions, and digital-download payloads.

Get a store's product

GET /api/stores/st_abc123/product
# → { "id": "pr_...", "title": "...", "price_cents": 2999, "currency": "USD", "variants": [...] }

Update a product

PATCH /api/stores/st_abc123/product
{
  "title": "Magnetic Phone Mount for Cars",
  "price_cents": 2400,
  "description_html": "<p>Stays put...</p>",
  "faq": [
    {"q": "Will it fit my phone?", "a": "Works with phones up to 6.9\""},
    {"q": "How does it stick to the vent?", "a": "Strong magnet + grip..." }
  ]
}

Add an image

POST /api/images/st_abc123/upload
# Multipart form upload, field name: "image"
# Returns: { "url": "https://images.instxnt.xyz/...", "r2_key": "..." }

AI generation

Kick off a job that generates product copy, FAQ entries, and SEO metadata from a product image plus a few hints. Jobs run async on Cloudflare Queues; poll for status.

Start a generation job

POST /api/generate/start
{
  "store_id": "st_abc123",
  "image_url": "https://images.instxnt.xyz/...",
  "hints": {
    "product_kind": "kitchen accessory",
    "tone": "casual",
    "audience": "home cooks"
  }
}
# → 202 { "job_id": "jb_xyz789" }

Poll for status

GET /api/generate/status/jb_xyz789
# Possible states: queued | running | succeeded | failed
# When succeeded:
# {
#   "status": "succeeded",
#   "output": {
#     "title": "...", "description": "...", "faq": [...],
#     "seo": { "title": "...", "description": "...", "keywords": [...] }
#   }
# }

Free-tier accounts can run 5 generations/day. Pro at $19/mo allows 100/day. Errors fail the job with a structured reason in error.code.

Checkout & Stripe Connect

Checkout sessions are created on demand for each buyer. instxnt uses Stripe Connect Express; funds settle to the seller's connected Stripe account, not to instxnt.

Create a checkout session

POST /api/checkout/create-session
{
  "store_id": "st_abc123",
  "variant_id": "vr_...",  // optional
  "quantity": 1,
  "success_url": "https://yourstore.instxnt.store/thank-you",
  "cancel_url": "https://yourstore.instxnt.store/cart"
}
# → { "session_id": "cs_test_...", "url": "https://checkout.stripe.com/c/pay/..." }

Most callers will not need this directly — instxnt's rendered storefronts handle session creation server-side. Use this when you're embedding checkout into a custom flow.

Payment fees

  • Free plan: 3% instxnt platform fee + Stripe (2.9% + 30¢ in the US).
  • Pro plan ($19/mo): 0% instxnt platform fee + Stripe processing only.
  • The platform fee is collected via Stripe's application_fee_amount; sellers see it itemized in their Stripe dashboard.

Orders

List orders for a store

GET /api/stores/st_abc123/orders?limit=50&cursor=...
# Returns paginated:
# {
#   "orders": [
#     {
#       "id": "or_...", "stripe_payment_intent": "pi_...",
#       "total_cents": 3000, "currency": "USD",
#       "buyer": { "email": "...", "name": "..." },
#       "shipping_address": {...},
#       "status": "paid" | "fulfilled" | "refunded",
#       "created_at": "2026-04-26T..."
#     }, ...
#   ],
#   "next_cursor": "..."
# }

Get a single order

GET /api/orders/or_...

Rate limits

  • Read endpoints: 600 requests/minute per session.
  • Write endpoints: 60 requests/minute per session.
  • Generation jobs: Daily quota by plan (5 Free / 100 Pro), enforced at /api/generate/start.
  • Image uploads: 30/hour per store.

Exceeding any limit returns HTTP 429 with Retry-After in seconds. Burst over the per-minute limits is allowed up to 2× for short windows.

Errors

Errors return a JSON body with a stable code field plus a human-readable message. Codes are stable across versions; messages are not.

// 400 Bad Request
{ "code": "validation_failed", "message": "price_cents must be a positive integer", "field": "price_cents" }

// 401 Unauthorized
{ "code": "missing_session", "message": "Authenticate via /api/auth/* first" }

// 403 Forbidden
{ "code": "not_owner", "message": "You do not own store st_abc123" }

// 404 Not Found
{ "code": "not_found", "message": "Store not found" }

// 429 Too Many Requests
{ "code": "rate_limited", "message": "Slow down", "retry_after": 12 }

// 500 Internal
{ "code": "internal", "message": "...", "request_id": "req_..." }

Always log request_id when you see a 500. It maps to a single line in our edge logs and is the fastest way to get help on a specific failure.

Webhooks

instxnt fires webhooks for order events (order.paid, order.refunded, order.fulfilled) so your downstream systems stay in sync.

Read the webhook docs →

JavaScript example

// Create a store, upload an image, kick off AI generation,
// poll until done, and update the product with the result.

const BASE = 'https://api.instxnt.xyz';
const COOKIE = process.env.INSTXNT_SESSION_COOKIE!;

async function api(path: string, init: RequestInit = {}) {
  const csrf = await fetch(`${BASE}/api/auth/csrf`, { headers: { Cookie: COOKIE } })
    .then(r => r.json())
    .then(j => j.token);
  return fetch(`${BASE}${path}`, {
    ...init,
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': csrf,
      Cookie: COOKIE,
      ...(init.headers || {}),
    },
  });
}

const store = await api('/api/stores', {
  method: 'POST',
  body: JSON.stringify({ name: 'Magnetic phone mount' }),
}).then(r => r.json());

const job = await api('/api/generate/start', {
  method: 'POST',
  body: JSON.stringify({
    store_id: store.id,
    image_url: 'https://images.instxnt.xyz/uploads/abc.jpg',
    hints: { product_kind: 'car accessory', tone: 'casual' },
  }),
}).then(r => r.json());

let status: any;
do {
  await new Promise(r => setTimeout(r, 2000));
  status = await fetch(`${BASE}/api/generate/status/${job.job_id}`, { headers: { Cookie: COOKIE } })
    .then(r => r.json());
} while (status.status === 'queued' || status.status === 'running');

if (status.status !== 'succeeded') throw new Error('Generation failed');

await api(`/api/stores/${store.id}/product`, {
  method: 'PATCH',
  body: JSON.stringify(status.output),
});

Spotted something wrong, missing, or out of date?

Email support@instxnt.xyz