Subscriptions

This page describes Áskell’s legacy subscription flow, where a subscription is created from a PlanVariant and stored as a legacy Subscription model. For the new approach built on catalogs, bundles, and subscription contracts, see Subscription Contracts V2.

All calls to the subscription API can be tried out in our Swagger documentation, which you can find here.

Authentication

Like the rest of Áskell’s legacy API, these endpoints require a secret API key or legacy token authentication.

Authorization: Api-Key your-secret-api-key

Overview of the main endpoints

Endpoint

Description

GET /api/subscriptions/

Retrieves a list of legacy subscriptions.

POST /api/subscriptions/

Creates a legacy subscription from nested data.

GET /api/subscriptions/{id}/

Retrieves a single legacy subscription.

PATCH /api/subscriptions/{id}/

Updates a limited set of fields on a subscription.

POST /api/subscriptions/{id}/cancel/

Cancels an active subscription.

POST /api/subscriptions/{id}/activate/

Activates a subscription that is not active.

POST /api/subscriptions/{id}/set_expiry/

Overrides the expiry of the last billing log.

POST /api/subscriptions/{id}/execute_billing/

Runs billing if the subscription is past its active_until.

GET /api/customers/{reference}/subscriptions/

Retrieves the subscriptions of a given customer.

POST /api/customers/{reference}/subscriptions/add/

Creates a subscription for a given customer.

POST /api/subscriptions/multi/

Creates or updates a customer and creates multiple subscriptions at once.

How the legacy flow works

A legacy subscription is based on a PlanVariant. The PlanVariant defines, among other things, the price, recurrence interval, currency, and optionally a payment processor. The amount can then be overridden on the subscription itself with amount or discount.

When a new legacy subscription is created, one of the following happens:

  1. If a trial period is offered, a trial billing log is created and the subscription is activated without a charge.

  2. If there is no trial period, a renewal billing log is created and the first charge is attempted immediately, unless the first billing date has been set in the future.

  3. If the amount is 0 ISK or a free product applies, the subscription can become active without a card being charged.

List subscriptions

GET /api/subscriptions/ returns the legacy subscriptions that belong to your account.

Supported options include:

  • search on reference

  • filtering on reference

  • type=light for a shorter response

  • ordering by active_until and start_date

Example:

curl "https://askell.is/api/subscriptions/?type=light&ordering=-start_date" \
  -H "Authorization: Api-Key your-secret-api-key"

Note

ordering and active_until apply only to this legacy flow. V2 contracts have neither active_from nor active_until, and their list does not support ordering. See Validity and cancellation in Subscription Contracts V2.

light responses include, among other things:

  • id

  • plan

  • customer_reference

  • start_date

  • active_until

  • last_billing_log

  • amount

  • cancelled

  • cancel_date

  • has_payment_plan

  • payment_plan_info

Retrieve a single subscription

GET /api/subscriptions/{id}/ returns a more detailed response, including billing_logs, delivery_address, and nested customer and plan data.

Direct creation with /api/subscriptions/

POST /api/subscriptions/ uses SubscriptionSerializer and expects nested plan and customer data. This endpoint is therefore usually less convenient for simple integrations than the customer-scoped create endpoint.

In practice, most integrations use either:

  • POST /api/customers/{reference}/subscriptions/add/ for a single subscription

  • POST /api/subscriptions/multi/ for multiple subscriptions at once

Create a subscription for a customer

The most common legacy creation flow is:

POST /api/customers/{reference}/subscriptions/add/

The request body can include, among other things:

Field

Description

plan

ID of the PlanVariant.

reference

Reference for the subscription.

description

Free-text description.

start_date

Optional first billing date or end of the trial period.

amount

Optional amount override.

discount

Optional discount, 0-100.

delivery_address

Optional delivery address.

If payment is required, the customer must already have a valid, verified payment method. If the subscription price is above 0 and there is no payment method, an error is returned.

Example:

  • python
import requests

API_KEY = 'your api key here'
headers = {
    "Authorization": f"Api-Key {API_KEY}",
}

data = {
    "plan": 12,
    "reference": "sub-2026-0001",
    "description": "Mánaðaráskrift",
    "discount": "10",
}

response = requests.post(
    "https://askell.is/api/customers/customer-123/subscriptions/add/",
    data=data,
    headers=headers,
)

Create multiple subscriptions at once

POST /api/subscriptions/multi/ updates or creates a customer and then creates more than one subscription in the same call.

This endpoint usually takes three main parts in the request body:

  • customer

  • payment_method

  • subscriptions

customer contains the customer data and can also include a delivery address. payment_method can be:

  • {"invoice": true}

  • a temporary token from a temporary payment method

  • raw card data in older integrations

  • an empty object if all subscriptions are 0 ISK

Each entry in subscriptions can contain:

  • plan

  • reference

  • description

  • amount

  • discount

  • start_date

Example:

{
  "customer": {
    "customer_reference": "customer-123",
    "first_name": "Jón",
    "last_name": "Jónsson",
    "email": "jon@example.com"
  },
  "payment_method": {
    "token": "temporary-payment-token"
  },
  "subscriptions": [
    {
      "plan": 12,
      "reference": "sub-magazine",
      "description": "Tímarit"
    },
    {
      "plan": 13,
      "reference": "sub-donation",
      "amount": "500"
    }
  ]
}

Update a subscription

PATCH /api/subscriptions/{id}/ does not allow arbitrary changes to every field, but mainly supports these overrides:

  • plan

  • amount

  • meta and other serializer fields that pass validation

If plan is changed on an active subscription and the new plan has reached its subscription cap (subscription limit), an error is returned.

Cancel, activate, and change the expiry date

Legacy subscriptions support the following management calls:

Endpoint

Description

POST /cancel/

Cancels a subscription that is in an active state.

POST /activate/

Reactivates a subscription if it has not expired.

POST /set_expiry/

Sets a new expiry on the last billing log.

POST /execute_billing/

Runs billing if active_until has passed.

set_expiry takes the field expires and supports both:

  • YYYY-MM-DD

  • YYYY-MM-DDTHH:MM:SS

On a subscription that has been migrated to a V2 contract, all of these calls reject the request, see Subscription contract accounts and migrated subscriptions.

Subscription contract accounts and migrated subscriptions

When the account’s subscription model is contracts (Subscription contracts only in the dashboard), the legacy endpoints no longer create new legacy subscriptions. The following calls then return a 400 error with code legacy_subscriptions_disabled, before a customer is created or updated and before anything is charged:

  • POST /api/subscriptions/

  • POST /api/subscriptions/multi/

  • POST /api/customers/{reference}/subscriptions/add/

  • POST /api/checkouts/ when the request includes plan. A checkout without plan, that is with payment_processor and currency to register a card or take a one-off amount, still works.

Payment pages that sell legacy plans reject purchases in the same way, with code legacy_subscriptions_disabled, before a card is verified or charged. The subscribe button (/subscribe-button/{plan_id}/) also rejects purchases before a customer is created or anything is charged, but it responds with an error page (400) for the buyer rather than a JSON response with code. See Payment pages.

Accounts with the subscription model legacy or dual create legacy subscriptions as before. On a contracts account, new subscriptions are created with POST /api/v2/subscription-contracts/, see Subscription Contracts V2.

{
  "error": "This account uses subscription contracts and no longer creates legacy subscriptions. Use POST /api/v2/subscription-contracts/ instead.",
  "code": "legacy_subscriptions_disabled",
  "v2_endpoint": "/api/v2/subscription-contracts/"
}

POST /api/customers/{reference}/subscriptions/add/ also returns "status": "error", like the other errors from that endpoint.

Once a legacy subscription has been migrated to a V2 contract, the contract handles its billing. The subscription’s responses then have billing_managed_by set to subscription_contract, and migrated_to_contract_id points to the contract. On such a subscription, the following calls return a 400 error with code subscription_managed_by_contract and change nothing, whatever the account’s subscription model:

  • POST /api/subscriptions/{id}/cancel/, regardless of the legacy subscription’s state

  • POST /api/subscriptions/{id}/activate/, regardless of the legacy subscription’s state

  • POST /api/subscriptions/{id}/execute_billing/

  • PATCH /api/subscriptions/{id}/ and PUT /api/subscriptions/{id}/

  • POST /api/subscriptions/{id}/set_expiry/

GET calls keep working. If the migration is reverted, billing_managed_by goes back to legacy_subscription and the calls work as before.

{
  "error": "This subscription's billing is managed by subscription contract 123. Cancel it with POST /api/v2/subscription-contracts/123/cancel/ instead.",
  "code": "subscription_managed_by_contract",
  "migrated_to_contract_id": 123,
  "v2_endpoint": "/api/v2/subscription-contracts/123/cancel/"
}

Field

Description

error

Description of the error, in English.

code

legacy_subscriptions_disabled or subscription_managed_by_contract. Integrations should act on this field rather than on the error text.

migrated_to_contract_id

ID of the V2 contract that handles the billing. Only present with subscription_managed_by_contract.

v2_endpoint

The V2 endpoint to use instead: POST /api/v2/subscription-contracts/ to create, POST /api/v2/subscription-contracts/{id}/cancel/ to cancel, and /api/v2/subscription-contracts/{id}/ for other changes.

Payment plans

Responses from the legacy flow can include payment plan information if the subscription has an active payment plan:

  • has_payment_plan

  • payment_plan_info

payment_plan_info can include, among other things:

  • total_installments

  • current_installment

  • remaining_installments

  • next_installment_date

  • progress_percentage

  • status

  • billing_period_start

  • billing_period_end

Common errors

Error

Description

Plan not found

The PlanVariant was not found or does not belong to the account.

Plan subscription limit reached

A new subscription would exceed the configured subscription cap.

Customer does not have a valid payment method

A paid subscription was requested without a valid payment method.

Discount must be a value in the range 0-100

The discount is outside the allowed range.

Cannot cancel subscription from ... state

An attempt was made to cancel a subscription from a state that does not allow it.

Cannot activate an expired subscription

An attempt was made to activate an expired subscription.

code: legacy_subscriptions_disabled

The account uses subscription contracts (contracts) and no longer creates legacy subscriptions. Use POST /api/v2/subscription-contracts/. See Subscription contract accounts and migrated subscriptions.

code: subscription_managed_by_contract

The subscription has been migrated to a V2 contract (migrated_to_contract_id) that handles its billing. Cancel or change the contract at the endpoint given in v2_endpoint.

A note on the new model

The legacy subscription endpoints continue to work for existing integrations on accounts with the subscription model legacy or dual. On an account that uses subscription contracts only, they refuse to create new legacy subscriptions, see Subscription contract accounts and migrated subscriptions. For new integrations that need a catalog, bundles, add-ons, price versions, payment processor selection, and billing run retries, Subscription Contracts V2 is recommended.