Skip to main content
By the end of this guide you’ll have a subscription product, a test subscriber, and you’ll know how to see who’s subscribed and cancel them. This runs in test mode. The “buyer” is you, paying with the test card.
There is no subscriptions.create(). You create a product once. The buyer pays. That payment is what starts the subscription.

Before you start

  • An AgentaOS account and a test API key (sk_test_...) from Settings → Developers → API Keys. See Accept your first payment if you haven’t.
  • Card and bank are on by default. Wallet-only accounts cannot sell subscriptions. KYB is only needed for live. See Go live.
Test card, typed on the hosted page only: 4242 4242 4242 4242, any future expiry, any CVC. Never send a card number to the API.

The one id

The dashboard calls this a product. The API creates it with paymentLinks.create. The id you pass around is linkId. linkId is what checkouts.create({ linkId }) wants. It is not the /pay/… slug.
1

Create the product once

One product per price. Catalog → Products, or in code:
amount: 19.99 means €19.99, not cents. Create this once, not once per buyer.
From the terminal: agenta products create --name "Pro" -a 19.99 -c EUR --subscription --interval month --json returns the same product; its id is the linkId. Needs agentaos 2.1 or newer (npm install -g agentaos@latest). agenta pay checkout still only makes a one-time checkout.
2

Get them to pay

Two ways. Same product. Same linkId.
3

Confirm they paid

Shared the /pay/… link? List subscriptions and find them by email:
Created a checkout for a logged-in user? Prefer the webhook. checkout.session.completed includes event.data.metadata. Your customerId is there. So is subscriptionId after they start the card form.
subscriptions.list() does not copy your metadata. Later renewals fire the same event without customerId. Look up the user by the subscription.id you stored the first time.Or poll checkouts.retrieve(sessionId) until completed, expired, or cancelled. Verify the signature: Handle webhooks.
unitAmountMinor is cents. 1999 means €19.99. Every other amount in this API is already decimal (19.99).
4

Status, invoices, cancel

Use the subscription.id.
Cancel does not refund. It stops the next renewal. They keep access until currentPeriodEnd. Pass { atPeriodEnd: false } (CLI: --now) to cancel immediately instead. Calling cancel again is a no-op.PDF: agentaos.invoices.downloadPdf(invoice.id) and agentaos.invoices.getReceipt(invoice.id).
5

Optional: a free trial

A trial is a property of the plan. Add trialPeriodDays (1 to 730) when you create it; nothing else changes.
The hosted checkout says “Due today €0.00, then €19.99/mo after your 14-day free trial”, saves the card, and creates the subscription as trialing. The first charge happens when the trial ends. checkout.session.completed fires at trial start with amount: "0", trial: true and trial_end; treat it as “trial started”, not as money received.
6

Optional: upgrade or downgrade

Each price is its own product. To move a subscriber, quote the change, show it, then apply it with the quote’s prorationDate.
An upgrade charges the prorated difference on the saved card now and the new price applies immediately. A downgrade charges nothing today; the subscriber keeps the current plan until effectiveAt, the current period end, and the subscription reports the switch as pendingPlanChange until then. To cancel a scheduled downgrade, change the plan to the current plan again (direction: 'revert', nothing charged). The target must be a different plan in the same currency and billing interval, and the subscription must be active or trialing. Each of these fires subscription.updated.

Verify it worked

  • subscriptions.list() shows status: 'active' and the right unitAmountMinor.
  • Shared-link buyer: they used /pay/… and appear under Customers.
  • Logged-in buyer: they landed on /checkout/…. The webhook has metadata.customerId and metadata.subscriptionId. subscriptions.list() does not contain customerId.
  • Cancel sets cancelAtPeriodEnd: true. Access stays until currentPeriodEnd.

Next steps

Handle webhooks

Verify the signature and handle the same event twice.

Checkouts

checkouts.create({ linkId }) and every field on the session.

Subscriptions

List, invoices, and cancel.

Go live

Take this product from test mode to real money.