NorelioDeveloper documentation

Manage a subscription

A one-hour link to the page where the buyer manages one subscription, with a button back to your app: request, response, return URL rules, the flow and test subscriptions.

POST /api/v1/subscriptions/{id}/manage-link returns a link to the page where the buyer manages ONE of your subscriptions: the same page the renewal and failed-charge emails lead to — the period, the next charge, cancelling at the end of the period. Put a "Manage subscription" button in your application and open the link when the user presses it. The subscription is named by its id from the customer state (subscriptions[].id); the body is optional.

The server API is for a partner whose application knows its own user: a payment is created from your server, with no button or panel on a page, and the user's state (subscriptions and purchases) is read by your own account reference. The key is the same as for the access check (API key tab in the cabinet), sent as Authorization: Bearer; the partner is identified by the key, there is no partner parameter. Rate limits are the same as for the access check: per your server's address and per key. Responses are JSON only and never cached. You can ask only about your own products and buyers: a foreign product or buyer and a non-existent one answer alike.

Request

http
POST https://app.norelio.eu/api/v1/subscriptions/{id}/manage-link
Authorization: Bearer {api key}
Content-Type: application/json
FieldDescription
returnUrlOptional. Where the "Return to the app" button on the page leads: an https address on one of the return sites allowed for your account, by the same rule as the return URL after payment, up to 2000 characters. Without it the page has no such button.
Example
curl -s -X POST "https://app.norelio.eu/api/v1/subscriptions/sub_4d5e6f/manage-link" \
  -H "Authorization: Bearer $NORELIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"returnUrl":"https://pawclaw.com/account"}'

The return address follows the rules of Return URL. A returnUrl that fails the check does not stop the link: it is created without the return button, and warnings carries return_url_rejected ("returnUrl was ignored: it is not an https address on one of the return sites allowed for your account. The link was created without the button that returns the buyer to your app.").

Response

201 Created with these fields:

FieldDescription
subscriptionIdThe subscription id you asked about.
urlThe address of the subscription page, on Norelio's payment domain. Open it in the user's browser as a normal navigation.
expiresAtUntil when the link works (ISO 8601): 60 minutes after it was created. After that the page says the link does not work; create a new link on every press of the button.
warningsWarnings, empty when everything was accepted. Each is an object with code and message.
testtrue for a test subscription (asked with the test key): the link leads to the test subscription page; false for a live one.
Example response
{
  "subscriptionId": "sub_4d5e6f",
  "url": "https://pay.lis-system.eu/subscription/eyJpZCI6InN1Yl80ZDVlNmYiLCJleHAiOjE3NzM0ODQ0MTN9.Qm9ndXNTaWduYXR1cmVFeGFtcGxl",
  "expiresAt": "2026-03-14T10:26:53.000Z",
  "warnings": [],
  "test": false
}

What the buyer sees

The page shows the subscription to whoever opens the link, without signing in, and lets them cancel it at the end of the period. Links in the emails Norelio sends the buyer keep working as before and do not expire; a link from the API lives 60 minutes and carries your return button. The return address is checked again every time the page is shown: remove the site from your allowed return sites, and the button disappears from links already issued.

Anyone who has the link can see and cancel that subscription. Create it when the user presses the button, show it to that signed-in user only, do not store it and do not write it to logs. Links are issued only for your own subscriptions: a foreign id and a non-existent one answer the same 404 subscription_not_found.

The whole flow

A "Manage subscription" button in your application:

  1. Your server reads the customer state by the user's account reference and takes the id of the subscription from subscriptions[].
  2. When the user presses the button, your server calls the manage-link endpoint with that id and, optionally, returnUrl — the page of your application to come back to.
  3. Send the user to url as a normal browser navigation. They see the subscription on Norelio's page and can cancel it; "Return to the app" brings them back to returnUrl.
  4. A cancellation by the buyer arrives as subscription.canceled for a product delivered by webhook, and as customer.state_changed for any product when it is on; the access check and the customer state show it at once.

The subscription id comes from Customer state; the cancellation arrives as described in Webhooks.

Test mode

With the test key, pass the id of a test subscription (it starts with test_): the link leads to the test subscription page on Norelio's payment domain (/test-checkout/subscription/…), marked as test mode, with a Cancel subscription button. Cancelling there sends subscription.canceled with test: true to your endpoint, as the cancel action of a test order does. The live key does not find test subscriptions, and the test key does not find live ones. See Test mode.

Errors

An error from any API endpoint is JSON of one shape: an error object with code (one of the codes below) and message (a short English sentence). A 429 or 503 response carries a Retry-After header. The access check keeps its own, older error shape.

StatusCodeWhen
429rate_limitedtoo many requests in a row, from your address or with your key
503rate_limiter_busythe request could not be counted against the rate limits, so nothing was done; send the same request again after the number of seconds in the Retry-After header
401invalid_api_keythe key is missing or does not match
400invalid_requestthe body or the query is malformed; message says what exactly
403direct_checkout_not_enabledcreating payments from your server is not yet enabled for your account: the platform first reviews your purchase terms (the same review as for direct checkout)
404product_not_foundno product with this id belongs to your account, or it cannot be bought right now
502checkout_failedthe payment provider could not start the payment; retry later
404not_foundno such endpoint
405method_not_allowedthe endpoint does not accept that method
403test_key_requiredthe endpoint works only with the test API key (it starts with nrl_test_), and the request came with the live key
404test_order_not_foundno test order with this id belongs to your account, or it has already been deleted: test orders live for 30 days
409test_action_not_allowedthe action is not possible in the test order's current state, such as a refund of an order not yet paid or a renewal of a canceled subscription; message names the reason
409insufficient_balancethe balance is lower than the amount to debit; nothing was debited, and the response carries the current balance in the balance field next to error
422idempotency_conflicta request with this idempotency key was already processed with a different body; use a new key for a new operation. A balance debit or grant answers 422 (idempotencyKey in the body), a payment created with the Idempotency-Key header answers 409
409idempotency_in_progressa payment request with this Idempotency-Key is still being processed; send the same request again after the number of seconds in Retry-After and you get its response
404subscription_not_foundno subscription with this id belongs to your account in this mode: a live key sees only live subscriptions, a test key only test ones
Example error
{
  "error": {
    "code": "direct_checkout_not_enabled",
    "message": "Creating payments from your server is enabled after the platform has reviewed your purchase terms. Ask support from the cabinet's Messages section."
  }
}