Getting Started
This guide walks the full chain once, server-side: create a product, price it with a plan, upsert a customer, save a card, start the subscription, and listen to the webhooks that confirm it. All calls use your access token (Authorization: Bearer at_live_…) against https://api-gateway.lunixi.com, and every list endpoint paginates with pageSize / pageToken.
Amounts are minor-unit integers (kuruş): "price": 49900 = ₺499.00.
Create a product
The product is the sellable thing; plans attach prices to it.
curl -X POST https://api-gateway.lunixi.com/api/v1/subscriptions/products \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Content-Type: application/json" \
-d '{
"code": "pro",
"name": "Pro",
"description": "For growing teams"
}'Create a plan
The plan carries the price, the currency, the optional trial and the billing rule — how often an invoice is generated.
curl -X POST https://api-gateway.lunixi.com/api/v1/subscriptions/plans \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Content-Type: application/json" \
-d '{
"productId": "<product-id>",
"code": "pro-monthly",
"name": "Pro Monthly",
"price": 49900,
"currency": "TRY",
"trialPeriodDays": 7,
"billing": {
"frequency": "MONTHLY",
"interval": 1,
"timezone": "Europe/Istanbul",
"lastDayOfMonthFix": true
}
}'See Plans for dunning fields (maxDunningAttempts, dunningScheduleDays, gracePeriodDays) and the full billing rule.
Upsert the customer
Customers are keyed by your externalId and created with PUT — call it as many times as you like, the same external id always resolves to the same customer. No "did I already create this user?" bookkeeping.
curl -X PUT https://api-gateway.lunixi.com/api/v1/subscriptions/customers \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Content-Type: application/json" \
-d '{
"externalId": "user-8421",
"name": "Ayşe",
"surname": "Yılmaz",
"email": "[email protected]",
"gsmNumber": "+905550000000"
}'You can later fetch by your own key with GET /api/v1/subscriptions/customers/by-external/user-8421.
Save a card
Card data never touches your server: collect it in the browser with the secure card form (the Portal SDK's cardForm or the hosted checkout form), which returns a paymentMethodToken. Then register the card against the customer:
curl -X POST https://api-gateway.lunixi.com/api/v1/subscriptions/cards \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Content-Type: application/json" \
-d '{
"customerId": "<customer-id>",
"paymentMethodToken": "<token-from-card-form>",
"brand": "VISA",
"bin": "454360",
"last4": "1234",
"expireMonth": 12,
"expireYear": 2028,
"isDefault": true
}'Saving a card requires 3DS. The cardholder authenticates with 3D Secure as part of card registration — a card cannot enter the vault unverified. Control the moment with verificationMode: ON_ADD verifies at save time, FIRST_CHARGE defers verification to the first charge.
Create the subscription
Bring it together: customer + plan + collection method. The idempotencyKey is required — retrying the same request with the same key never creates a second subscription.
curl -X POST https://api-gateway.lunixi.com/api/v1/subscriptions/subscriptions \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Content-Type: application/json" \
-d '{
"customerId": "<customer-id>",
"planId": "<plan-id>",
"idempotencyKey": "sub-user-8421-pro-monthly-001",
"collectionMethod": "AUTO_CHARGE",
"paymentCardId": "<card-id>"
}'With a 7-day trial the subscription starts in trial and converts automatically; pass skipTrial: true to bill immediately.
Listen to webhooks
Don't poll — react. The two events that matter first:
| Event | When |
|---|---|
subscription.created | The subscription record exists (possibly in trial). |
subscription.activated | The subscription is live — provision access now. |
Down the line you'll also want subscription.trial_will_end, invoice.paid, invoice.payment_failed and subscription.canceled. Configure your endpoint and verify signatures as described in Webhooks.
Errors
| Status | Meaning | What to do |
|---|---|---|
400 | Validation failed (missing idempotencyKey, bad billing rule, negative price…) | Fix the request body; the response lists the failing fields. |
401 | Missing or invalid API key | Send Authorization: Bearer at_live_…. |
404 | Referenced object not found (productId, planId, customerId, paymentCardId) | Create the dependency first — follow the step order above. |
409 | Conflict (duplicate code, idempotency replay with a different body) | Use a fresh code / a fresh idempotencyKey, or send the identical original body. |
Next steps
- Pricing & Portal SDK — do all of the above from your own pricing page
- Subscriptions — cancel, pause, resume, change plan
- Invoices — retry a failed charge, void an invoice
- Usage & Entitlements — metered features and quota checks