Usual Docs
View as Markdown

REST API

Base address: https://regular-subs.fly.dev/api/v1. Send your key as Authorization: Bearer <key>. Responses are JSON and carry an x-request-id header.

The machine-readable version is /api/v1/openapi.json (OpenAPI 3.1).

Changes have their own endpoints, listed with the reads for the same job. The body is the change's input and path parameters fill in the rest. Add ?preview=true to get a plan instead of applying, and X-Change-Reason to say why (agents must). Every change takes an Idempotency-Key header for safe retries.

Start here

List the changes a key can make

GET /api/v1/commands

Every change this key can make, with its input schema, risk and whether it can be undone.

Permission: any key · Returns: { commands: [{ name, description, risk, reversible, input, http }] }

See how subscriptions are doing

GET /api/v1/overview

How the store's subscriptions are doing: whether everything is working, active subscribers, monthly recurring revenue, charges due this week, and setup progress. The best first call.

Permission: subscriptions:read · Returns: { checks, metrics, trend, forecast, topProducts, activity, setup }

Subscriptions

Find subscriptions

GET /api/v1/subscriptions

Find subscriptions, newest first. Filters combine.

Permission: subscriptions:read · Returns: { subscriptions: Subscription[], total, nextCursor }

ParameterInTypeDescription
statusquerystringComma-separated: ACTIVE, PAUSED, CANCELLED, EXPIRED, FAILED.
customerIdquerystringA Shopify customer GID.
productIdquerystringHas a line for this Shopify product GID.
variantIdquerystringHas a line for this Shopify variant GID.
sellingPlanIdquerystringHas a line on this selling plan.
nextBillingFromquerystringNext charge on or after this time (ISO 8601).
nextBillingToquerystringNext charge on or before this time (ISO 8601).
queryquerystringProduct title, SKU, or the end of a contract or customer id.
limitqueryintegerResults per page, 1–100.
cursorquerystringnextCursor from the previous page.

Look up a subscription

GET /api/v1/subscriptions/:id

One subscription: its status, lines, prices, schedule, next charge and last payment.

Permission: subscriptions:read · Returns: { subscription: Subscription }

ParameterInTypeDescription
idpathstringOur id, or the Shopify contract GID (URL-encoded).

See a subscription's history

GET /api/v1/subscriptions/:id/history

What happened to one subscription, newest first, with who did each thing: the customer, staff, an agent, an API key or Shopify.

Permission: subscriptions:read · Returns: { subscriptionId, events: [{ at, type, by, to, reason, ... }] }

ParameterInTypeDescription
idpathstringOur id, or the Shopify contract GID.

GET /api/v1/subscriptions/:id/portal-link

A link that opens this subscription in the customer portal on the store's site, or goes straight to cancelling it. Safe to send: the customer logs in first and only ever sees their own subscriptions.

Permission: subscriptions:read · Returns: { subscriptionId, view, url, note }

ParameterInTypeDescription
idpathstringOur id, or the Shopify contract GID.
viewquerystringmanage (default) or cancel.

Re-check subscriptions against Shopify

POST /api/v1/subscriptions/reconcile

Re-check every subscription against Shopify and fix anything out of date. Runs in the background; check progress with get_sync_status. Safe to run any time: it only reads from Shopify and updates our copy.

Permission: subscriptions:sync · Risk: low · Undo: no · Input

Skip the next delivery

POST /api/v1/subscriptions/:subscriptionId/skip

Skip the next delivery. The next charge moves on by one delivery interval (e.g. a month). Good for customers with too much product who'd otherwise cancel.

Permission: subscriptions:write · Risk: low · Undo: yes · Input

Move the next delivery

POST /api/v1/subscriptions/:subscriptionId/reschedule

Move the next delivery to a specific date (tomorrow to a year out). Later deliveries follow on from the new date at the usual interval.

Permission: subscriptions:write · Risk: low · Undo: yes · Input

Pause a subscription

POST /api/v1/subscriptions/:subscriptionId/pause

Pause a subscription: no charges or deliveries until it's resumed.

Permission: subscriptions:write · Risk: medium · Undo: yes · Input

Resume a subscription

POST /api/v1/subscriptions/:subscriptionId/resume

Resume a paused subscription. If its next delivery date has passed, the next delivery is tomorrow at the usual time.

Permission: subscriptions:write · Risk: low · Undo: yes · Input

Cancel a subscription

POST /api/v1/subscriptions/:subscriptionId/cancel

Cancel a subscription. No more charges or deliveries. Record why: it drives which save offers are worth making. Customers can always cancel in one step.

Permission: subscriptions:write · Risk: high · Undo: no · Input

Change a quantity

POST /api/v1/subscriptions/:subscriptionId/quantity

Change how many of an item come in each delivery. The price per item stays the same; the charge changes with the quantity.

Permission: subscriptions:write · Risk: low · Undo: yes · Input

Change how often it delivers

POST /api/v1/subscriptions/:subscriptionId/frequency

Change how often deliveries come, to another frequency on the same offer (e.g. every 4 weeks instead of every 2). The next charge date stays; later ones follow the new frequency.

Permission: subscriptions:write · Risk: low · Undo: yes · Input

Swap to another variant

POST /api/v1/subscriptions/:subscriptionId/variant

Switch an item to another variant of the same product (size, flavour, grind). The subscription discount carries over to the new variant's price.

Permission: subscriptions:write · Risk: low · Undo: yes · Input

Change the shipping address

POST /api/v1/subscriptions/:subscriptionId/address

Ship future deliveries to a new address. The shipping price and method stay as they are, and Shopify refuses an address the store doesn't ship to. The address isn't kept in the activity log.

Permission: subscriptions:write · Risk: low · Undo: no · Input

Cancellation offers

Get the offer for a customer who wants to cancel

GET /api/v1/subscriptions/:id/save-offer

The one offer for a customer who wants to cancel, for their reason (skip, deliver later, less often, pause, swap, or a discount the store allows). Null when there's nothing worth offering. Share it once; if they say no, cancel.

Permission: subscriptions:read · Returns: { offer: { kind, heading, detail, accept, ... } | null }

ParameterInTypeDescription
idpathstringOur id, or the Shopify contract GID.
reasonquerystringtoo_much, too_expensive, taking_break, not_needed, didnt_like or other.

See how cancellation offers are doing

GET /api/v1/retention

How cancellation offers are doing: customers shown an offer, how many stayed, the save rate and monthly revenue kept, by reason and by offer.

Permission: subscriptions:read · Returns: { days, shown, saved, cancelled, saveRate, revenueKept, byOffer, byReason }

ParameterInTypeDescription
daysqueryintegerHow far back to look. Default 30.

Keep a customer with an offer

POST /api/v1/subscriptions/:subscriptionId/save

Take the offer shown to a customer who was about to cancel (skip, deliver later, less often, pause, swap, or a discount) instead of cancelling. Get the offer first with get_save_offer for the customer's reason; only that offer can be accepted. Recorded as a save in the retention report.

Permission: subscriptions:write · Risk: medium · Undo: no · Input

Offers

List offers

GET /api/v1/offers

The subscription offers shoppers can choose on product pages: name, frequencies, discount and products.

Permission: offers:read · Returns: { offers: Offer[] }

Look up an offer

GET /api/v1/offers/:id

One offer.

Permission: offers:read · Returns: { offer: Offer }

ParameterInTypeDescription
idpathstringOur id, or the Shopify selling plan group GID.

Create an offer

POST /api/v1/offers

Offer subscriptions on products: a name, one or more delivery frequencies, and an optional discount. Shoppers see it on the product page and choose a frequency at checkout.

Permission: offers:write · Risk: medium · Undo: yes · Input

Change an offer

PATCH /api/v1/offers/:id

Change an offer's name, description, delivery frequencies, discount or products. Existing subscriptions keep the terms they started with; changes apply to new subscriptions.

Permission: offers:write · Risk: medium · Undo: yes · Input

Stop offering a subscription

DELETE /api/v1/offers/:id

Stop offering a subscription option. It disappears from product pages. Existing subscriptions keep running on the terms they started with.

Permission: offers:write · Risk: high · Undo: no · Input

Billing

See how billing is going

GET /api/v1/billing/summary

Charges, failed payments, retries and what's due next.

Permission: billing:read · Returns: { due, retrying, failed, recent, ... }

List charges and payment attempts

GET /api/v1/billing/cycles

Billing cycles (one per renewal) with their payment attempts, newest first.

Permission: billing:read · Returns: { cycles: Cycle[], nextCursor }

ParameterInTypeDescription
statequerystringComma-separated: scheduled, charging, retry_scheduled, succeeded, exhausted, skipped, cancelled.
subscriptionIdquerystringOnly this subscription's cycles.
limitqueryintegerResults per page, 1–100.
cursorquerystringnextCursor from the previous page.

POST /api/v1/subscriptions/:subscriptionId/payment-update

Email the customer a secure Shopify link to update the card on this subscription. Use it after a declined or expired card, or when a customer asks. At most once an hour per subscription.

Permission: subscriptions:write · Risk: low · Undo: no · Input

Retry a failed payment now

POST /api/v1/billing/cycles/:cycleId/retry

Retry a failed charge now instead of waiting for the next scheduled retry, e.g. after the customer says they've updated their card. Charges the customer's payment method.

Permission: billing:write · Risk: financial · Undo: no · Input

Customer emails

See which customer emails were sent

GET /api/v1/notifications

Customer emails sent, skipped or failed, newest first, with why. Recipients are masked.

Permission: subscriptions:read · Returns: { notifications: [...], problems: { missingEmailAccess } }

ParameterInTypeDescription
subscriptionIdquerystringOnly this subscription's emails.
limitqueryintegerResults per page, 1–100.

Imports

List imports

GET /api/v1/imports

Subscriptions brought in from another app: each upload or fetch, its status and counts.

Permission: imports:read · Returns: { imports: Import[] }

Check an import

GET /api/v1/imports/:id

One import: problems grouped by kind with examples, warnings, a preview of what will be created, failures, and how many are waiting to bill.

Permission: imports:read · Returns: { import: ImportDetail }

ParameterInTypeDescription
idpathstringThe import's id.

Upload subscriptions to import

POST /api/v1/imports

Upload a subscriptions export from another app (Recharge, Skio, Appstle, Loop, Seal, Shopify Subscriptions or a spreadsheet). Nothing is created yet: each subscription is checked against the store (customer, saved card, products, date) in the background. Read the import to see what's ready and what needs fixing, then run imports.start.

Permission: imports:write · Risk: low · Undo: no · Input

Create the imported subscriptions

POST /api/v1/imports/:importId/start

Create the import's ready subscriptions in Shopify, keeping each one's next charge date, price and schedule. They're created in the background, one at a time. They don't bill until imports.release_billing, so the old app has time to be switched off. Subscriptions with problems are left out.

Permission: imports:write · Risk: medium · Undo: no · Input

Start billing imported subscriptions

POST /api/v1/imports/:importId/release-billing

Start billing an import's subscriptions here. Run it only once the old app has stopped charging them (turned off or uninstalled), or customers will be charged twice. Any whose charge date passed while waiting move to their next date, since the old app charged them.

Permission: imports:write · Risk: high · Undo: no · Input

Throw away an import

DELETE /api/v1/imports/:importId

Throw away an import that hasn't created anything yet, e.g. to upload a corrected file. Imports that have created subscriptions can't be discarded.

Permission: imports:write · Risk: low · Undo: no · Input

Connect Recharge

POST /api/v1/imports/connections

Connect the app the store is moving from with an API token from it, so subscriptions (and cards held at Stripe, Braintree or Authorize.net) can be fetched directly, and cancelled there when billing switches over. For Recharge the token needs: read_subscriptions, read_customers, read_payment_methods, write_subscriptions. The token is stored encrypted and never shown again.

Permission: imports:write · Risk: medium · Undo: no · Input

Disconnect Recharge

DELETE /api/v1/imports/connections/:source

Remove the stored API token for the app the store moved from. Delete the token in that app too.

Permission: imports:write · Risk: low · Undo: no · Input

Fetch subscriptions from Recharge

POST /api/v1/imports/connections/:source/fetch

Fetch every active subscription from the connected app (Recharge) and check them against the store, like an upload but always current. Nothing is created yet. Read the import, then run imports.start.

Permission: imports:write · Risk: low · Undo: no · Input

Plans and activity

Preview a change

POST /api/v1/plans

Preview any command without applying it. Returns exactly what would change and whether it needs a person's approval.

Permission: any key · Returns: { status: "planned", plan: Plan }

Takes an Idempotency-Key header for safe retries.

Check a preview

GET /api/v1/plans/:id

A plan: its preview, status (pending, awaiting_approval, applied, rejected, stale, expired) and what to do next.

Permission: any key · Returns: { plan: Plan }

ParameterInTypeDescription
idpathstringThe plan's id.

Apply a preview

POST /api/v1/plans/:id/apply

Apply a plan exactly as previewed. Refused if anything changed since it was made.

Permission: any key · Returns: { status: "applied", auditId, diff, result } or { status: "planned" } while it waits for approval

ParameterInTypeDescription
idpathstringThe plan's id.

Takes an Idempotency-Key header for safe retries.

See every change

GET /api/v1/activity

Every change ever applied, newest first: what, who, why, and whether it can be undone.

Permission: audit:read · Returns: { activity: [...], nextCursor }

ParameterInTypeDescription
limitqueryintegerResults per page, 1–100.
beforequerystringnextCursor from the previous page.

Undo a change

POST /api/v1/activity/:id/undo

Reverse a change by running its recorded inverse as a new change. Agents get a plan to apply.

Permission: any key · Returns: Same as any change: applied, or a plan

ParameterInTypeDescription
idpathstringThe activity entry's id.

Takes an Idempotency-Key header for safe retries.

Store

Read store settings

GET /api/v1/settings

Store settings: sender name, support email, retry schedule, customer emails, cancellation offers.

Permission: settings:read · Returns: { settings: Settings }

Check the last re-sync with Shopify

GET /api/v1/sync-runs

Recent re-checks of our data against Shopify: when, how many were checked, and how many were out of date.

Permission: subscriptions:read · Returns: { runs: [...] }

Change store settings

PATCH /api/v1/settings

Change store-wide subscription settings: sender name, support email, the failed-payment retry schedule, what happens after the final retry, and which customer emails are sent. Only the fields you pass change.

Permission: settings:write · Risk: medium · Undo: yes · Input

Connect Klaviyo

POST /api/v1/integrations/klaviyo

Send subscription events (started, cancelled, upcoming charge, order placed, payment failed...) to the store's Klaviyo account, for its own flows. The private API key needs: Accounts: Read, Events: Full, Profiles: Full.

Permission: integrations:write · Risk: medium · Undo: no · Input

Disconnect Klaviyo

DELETE /api/v1/integrations/klaviyo

Stop sending subscription events to Klaviyo and remove the stored key.

Permission: integrations:write · Risk: low · Undo: no · Input