Subscription Contracts V2
Subscription Contracts V2 is Askell’s new subscription model. It is separate from the older PlanVariant / Subscription flow and is instead built around a catalog, prices, bundles, quotes, checkout flows, and billing runs.
All calls to V2 API endpoints require a secret API key:
Authorization: Api-Key your-secret-api-key
Older token-based authentication is still supported in some V2 calls for compatibility with older integrations. New integrations should use secret API keys.
Quick overview of a typical flow
A common integration flow is this:
Fetch products, prices, or bundles from the catalog.
Calculate a quote with
POST /api/v2/subscription-offer-quotes/.Fetch eligible payment processors with
POST /api/v2/payment-processor-options/.Create a
checkoutwithPOST /api/v2/checkouts/.Finalize the
checkoutwithPOST /api/v2/checkouts/{token}/finalize/.Monitor the status of the
checkoutand, when applicable, the initial billing run.
If the integration does not need payment pre-processing, steps 3-5 can be skipped and the contract can be created directly with POST /api/v2/subscription-contracts/.
See also
For the legacy subscription flow based on PlanVariant and Subscription, see Subscriptions.
Warning
checkout_url in V2 is not a public hosted payment page. It points to the API URL for the checkout object itself and is therefore suitable for system-to-system integrations, not for directly redirecting a user in a browser.
When to use V2
V2 should be used when selling products and subscriptions from a catalog instead of older plans and PlanVariant identifiers. This includes:
When selling individual products or a combination of products as a subscription contract.
When using bundles with selectable prices and quantities.
When price changes need to be scheduled with price versions.
When recurring billing runs need clearer history and retry support.
Legacy payment pages and the older /api/subscriptions/ flow continue to use the legacy Subscription model and are documented separately under Subscriptions.
Core objects
The main V2 objects are these:
Object |
Description |
|---|---|
|
A product in the catalog. |
|
A stable price identifier that a contract is linked to. |
|
A time-bounded price version that defines an amount for a period. |
|
A sellable bundle that combines one or more products. |
|
The new persistent subscription contract. |
|
A single billing run on a contract. |
|
A single attempt to collect a billing run. |
|
An intermediate object that stores the quote and payment prerequisites before the contract is created. |
Before you start
Before a payment flow can be completed or a contract can be created, the following must already exist:
The customer must already exist in Askell.
A payment processor (
AccountPaymentProcessor) must be configured for the relevant currency and payment method.If the
checkout/finalizeflow is used, the customer must already have a verified payment method that matches the selected payment processor.
See also Prerequisites before calling finalize, which explains in more detail what is validated immediately before finalize creates the contract and billing.
Catalog lookup
Use the following endpoints to fetch products and prices from the catalog:
curl https://askell.is/api/v2/catalog/products/ \
-H "Authorization: Api-Key your-secret-api-key"
curl "https://askell.is/api/v2/catalog/prices/?billing_type=recurring¤cy=ISK" \
-H "Authorization: Api-Key your-secret-api-key"
Common catalog filters:
Filter |
Description |
|---|---|
|
By default only active records are shown. |
|
Product or bundle reference, depending on the endpoint. |
|
Product ID or product reference on the price endpoint. |
|
Price currency. |
|
|
|
Recurrence type for recurring prices. |
Price versions
In V2, a contract is linked to a stable CatalogPrice identifier, while the amount itself can change over time through CatalogPriceVersion. This makes it possible to schedule price changes in advance without moving the customer to a new price identifier.
Responses from catalog price endpoints include, among other things, the following fields:
Field |
Description |
|---|---|
|
The current display amount for the price. |
|
The ID of the price version currently considered active. |
|
Past, current, and future price versions. |
|
The start of the validity period for the currently displayed version. |
|
The end of the validity period for the currently displayed version. |
Integrations that only need the current amount can generally read unit_amount. Integrations that want to display or schedule price changes should use versions.
Bundles and price selection
Bundles are fetched from dedicated endpoints:
curl https://askell.is/api/v2/bundle-templates/ \
-H "Authorization: Api-Key your-secret-api-key"
curl https://askell.is/api/v2/bundle-templates/42/ \
-H "Authorization: Api-Key your-secret-api-key"
A bundle can contain a fixed product setup, allow price selection for specific bundle items, or use automatically active recurring prices on a related product.
Add-ons
Once a bundle has been selected and the relevant price selections provided, you can query which add-on products or add-on bundles are available:
curl https://askell.is/api/v2/bundle-templates/42/addons/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"bundle_item_selections": [
{
"bundle_item": 100,
"selected_price": 200
}
]
}'
Quote and request data
The same basic fields are used repeatedly in quotes, checkout flows, and direct contract creation.
Field |
Description |
|---|---|
|
The ID of the selected bundle. |
|
Bundle quantity. |
|
Price or quantity selections for individual items within a bundle. |
|
Directly selected recurring products without a bundle. |
|
One-time products or products that should only appear on the first billing run. |
|
Controls whether the initial period is not billed, billed immediately, or added to the next invoice. |
|
Add-on products selected through add-on rules. |
|
Add-on bundles selected through add-on rules. |
Only one primary input form for recurring products may be used at a time:
bundle_templateitems
initial_items are only used on the initial billing. They do not become persistent SubscriptionContractItem rows. See also Direct contract creation.
Shipping selection
An account can connect a shipping provider (Dropp, Pósturinn or in-store pickup) and define shipping options in the dashboard. When the account offers active shipping options and the cart contains a product that is not marked as electronic, POST /api/v2/checkouts/ must include a shipping object:
{
"shipping": {
"option": 12,
"location_id": "9591",
"location_name": "Póstbox Hallveigarstíg",
"location_address": "Hallveigarstíg 1, 101 Reykjavík"
}
}
option is the ID of an active shipping option on the account. location_id is required for options that need a location choice, for example parcel lockers or Dropp pickup points. On finalize the selection is stored as an immutable snapshot on the contract and returned as shipping_selection in contract responses, with the name, price, service code and chosen pickup location as they were at the time of purchase.
A shipping option can have a free-shipping threshold (free_above_amount). When the order total reaches the threshold, the shipping price is recorded as zero in the snapshot.
Note
The shipping price (price_amount) is only recorded on the snapshot in this version; it is not added to billing runs and is therefore not charged. Do not present it to customers as a fee that will be collected.
Fixed billing date
Recurring monthly or yearly prices can have billing_day_of_month. If more than one recurring price in the same quote or contract has such a day, they must reference the same day of month. Prices fixed to different days cannot be mixed, for example one price on day 1 and another on day 15 of the month.
Prices with a fixed day can, however, be mixed with prices without billing_day_of_month. Prices without their own day follow the single fixed day present in the contract.
Quote before creating the contract
POST /api/v2/subscription-offer-quotes/ builds a non-persistent quote from the request data and returns a summary of recurring lines, initial lines, taxes, totals, and the estimated billing schedule.
For recurring items, the response now distinguishes:
first_period_recurring_*: what is charged in the first run, including initial proration when the first run is shorter than a normal renewal period.recurring_*: the normal renewal amount after the first shorter period has passed.
Individual recurring lines also expose service_period_start_at, service_period_end_at, proration_factor, and renewal_line_* so an integration can clearly show the difference between the first billing and later renewals.
Example quote for a bundle:
curl https://askell.is/api/v2/subscription-offer-quotes/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"bundle_template": 42,
"bundle_quantity": 2,
"bundle_item_selections": [
{
"bundle_item": 100,
"selected_price": 200
}
],
"additional_items": [
{
"rule_id": 300,
"price": 400,
"quantity": 1
}
],
"initial_items": [
{
"price": 500,
"quantity": 1
}
]
}'
A shortened response might look like this:
{
"input_mode": "bundle",
"bundle_template_id": 42,
"bundle_quantity": 2,
"currency": "ISK",
"period_start_at": "2026-05-20T00:00:00Z",
"period_end_at": "2026-06-20T00:00:00Z",
"subtotal_amount": "4500.0000",
"tax_amount": "0.0000",
"total_amount": "4500.0000",
"first_period_recurring_subtotal_amount": "4000.0000",
"first_period_recurring_tax_amount": "0.0000",
"first_period_recurring_total_amount": "4000.0000",
"recurring_subtotal_amount": "4000.0000",
"recurring_tax_amount": "0.0000",
"recurring_total_amount": "4000.0000",
"billing_schedule_preview": {
"kind": "interval"
},
"recurring_items": [
{
"key": "bundle-item-100",
"source": "bundle",
"creates_contract_item": true,
"price_id": 200,
"product_id": 10,
"product_name": "Vefáskrift",
"billing_type": "recurring",
"quantity": 2,
"unit_amount": "2000.0000",
"service_period_start_at": "2026-05-20T00:00:00Z",
"service_period_end_at": "2026-06-20T00:00:00Z",
"proration_factor": "1.000000",
"line_total_amount": "4000.0000",
"renewal_line_total_amount": "4000.0000"
}
],
"initial_lines": [
{
"key": "initial-item-500",
"source": "initial_items",
"creates_contract_item": false,
"price_id": 500,
"product_id": 11,
"product_name": "Áskrifendagjöf",
"billing_type": "one_time",
"quantity": 1,
"unit_amount": "500.0000",
"line_total_amount": "500.0000"
}
]
}
Example quote for direct products without a bundle:
curl https://askell.is/api/v2/subscription-offer-quotes/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"currency": "ISK",
"items": [
{
"price": 200,
"quantity": 1
}
],
"initial_items": [
{
"price": 500,
"quantity": 1
}
]
}'
Direct contract creation
POST /api/v2/subscription-contracts/ creates a V2 contract directly. This flow is suitable when the integration has already confirmed the order and does not need to run payment pre-processing in the same call.
You can submit, among other things:
customer_referencecurrencybundle_templateoritemsbundle_item_selectionsadditional_itemsadditional_bundlesinitial_itemsinitial_billing_modemetadatapayment_processor_overrideif the contract should be pinned to a specific payment processor
initial_items do not become persistent SubscriptionContractItem rows. They are only placed on the first billing run lines if the initial billing is based on them.
initial_billing_mode makes initial billing explicit:
noneDefault value. The contract and recurring contract items are created, and
next_billing_atis set from the next billing time of the contract items. No billing run or initial proration line is created for the period from contract creation until the first scheduled billing. This is suitable when the initial period should be free of charge or is billed outside Áskell.create_initial_runCreates a scheduled initial
BillingRunwith recurring lines for the first period and any one-timeinitial_items. The first billing follows the same initial-proration rules asPOST /api/v2/subscription-offer-quotes/and can therefore be lower than therecurring_*amounts if the contract starts inside a shorter initial period.next_invoiceDoes not create an initial
BillingRun. Instead, initial proration lines for the recurring products are stored as pending lines and targeted at the first scheduled billing. When the first regular billing run is created, it includes both the initial period and the next regular period. This is suitable when service should start immediately but the initial period should be prorated onto the first due date.
initial_items are only allowed with initial_billing_mode=create_initial_run. They are not allowed with none or next_invoice because they do not become persistent contract items and therefore cannot be moved automatically to the next regular billing.
Example: service starts immediately but the initial period is billed on the first due date:
curl https://askell.is/api/v2/subscription-contracts/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"customer_reference": "customer-123",
"currency": "ISK",
"items": [
{
"price": 200,
"quantity": 1
}
],
"initial_billing_mode": "next_invoice"
}'
If the contract is created on May 21 and the first scheduled billing is June 1, the prorated period from May 21 to June 1 is added to the June 1 billing together with the regular period from June 1 to July 1.
Fetch and update a contract
A contract is fetched from:
GET /api/v2/subscription-contracts/{id}/
The contracts of the account are fetched from GET /api/v2/subscription-contracts/. The list supports the following filters:
Filter |
Description |
|---|---|
|
Contract state: |
|
The customer ID. |
|
The customer reference. |
Note
The list supports no ordering parameter and no filters other than those listed above. Unknown parameters do not return an error, they are simply ignored, so for example ?ordering=-created_at, ?reference=... or ?legacy_subscription_id=... return the list unchanged. The ordering is fixed: newest contracts first (-created_at, -id). This differs from the legacy GET /api/subscriptions/, which supports ordering on active_until and start_date.
Fields in the contract response
The same response format is used for contract creation, a single contract, lists and lifecycle calls. The main fields are these:
Field |
Description |
|---|---|
|
The contract ID. |
|
The customer ID and the nested customer data. |
|
The customer reference. |
|
|
|
Contract currency. |
|
Whether the contract keeps billing. Becomes |
|
Increments on every change to the contract. |
|
The billing anchor time. |
|
The time zone and time of day that billing runs at. |
|
When the billing period advances: |
|
The next scheduled billing. |
|
Trial period, if applicable. |
|
|
|
|
|
|
|
|
|
Whether service should be delivered right now, and the overall service state ( |
|
A summary of the service entitlements on the contract. |
|
Billing on the contract needs attention, for example after a failed charge. |
|
Whether a pause is scheduled in the future, and the date the pause runs until. |
|
Recorded pauses on the contract. |
|
Contract items with price, quantity and service periods. |
|
The latest billing run, with |
|
The initial billing run, if one was created. |
|
The active discount on the contract, if any. |
|
Delivery address and the chosen shipping option. |
|
IDs of legacy subscriptions that were migrated into the contract. |
|
Free-form data belonging to the integration. |
|
URL of the subscriber page for the contract. |
|
Creation and modification time. |
subscriber_page is the URL of the subscriber page for the contract, where the customer can manage their own subscription. It is the same URL that is shown in the dashboard and the field is read-only.
{
"id": 33,
"subscriber_page": "https://askell.is/change_contract/<token>/"
}
Validity and cancellation
Important
V2 contracts have neither active_from nor active_until. active_until is a field on the legacy Subscription model and is derived from its last billing log. The equivalent information in V2 comes from the contract state, the service periods of the items and the billing runs.
To answer the question “is this subscription active, and for how long?” use:
stateandservice_active/service_statefor the current status.next_billing_atfor the next billing.latest_billing_run.period_end_atfor the end of the period that was last billed.items[].current_service_period_end_atanditems[].entitled_untilfor the service entitlements of individual items.paused_untilif the contract is paused.
Cancellation is expressed in four fields:
Field |
Meaning |
|---|---|
|
|
|
The time the cancellation takes or took effect. Set both for a scheduled and an immediate cancellation. |
|
The time the cancellation was actually applied. |
|
The time the contract ended. |
Typical cases:
POST /cancel/withcancel_at_period_end=true:statestaysactive,cancel_at_period_endbecomestrue,cancel_atis set to the end of the current period, andcanceled_atandended_atremainnull.POST /cancel/with acancel_atin the future:cancel_at_period_endisfalse,cancel_atholds the chosen time, andcanceled_atandended_atremainnull.POST /cancel/withoutcancel_at_period_endandcancel_at: the cancellation is immediate.statebecomescanceled, all three time fields are set to the current time,recurringbecomesfalseandnext_billing_atbecomesnull.When a scheduled cancellation is applied:
statebecomescanceled,cancel_at_period_endgoes tofalse,canceled_atis the time the cancellation ran, andended_atis the time the cancellation was due to take effect.POST /restart/on a contract with a scheduled cancellation clearscancel_at_period_endandcancel_at.
A contract can also end without a cancellation, for example when a contract for a one-time purchase concludes. Then state becomes ended and ended_at is set, while canceled_at stays null.
The same fields are included in subscription_contract.* webhooks, see Webhooks.
List pagination
V2 list endpoints only use pagination if page_size is provided.
page_sizeenables paginationthe default
page_sizeis10when pagination is enabledthe maximum
page_sizeis1000pageselects the page
Example:
GET /api/v2/subscription-contracts/?page_size=25&page=2
When pagination is enabled, the response uses the standard shape with count, next, previous, and results. If page_size is not provided, an unpaginated list is returned.
The V2 detail endpoint only allows limited updates with PATCH. Only metadata, payment_processor_override, delivery_address, accounting_department and accounting_cost_center can be changed there; other fields return a 400 error. The contract is not intended to be a freely editable order draft, but rather a business-critical state that should evolve through defined lifecycle calls.
Item changes on an active contract
Note
Updated August 19 2026: unit_amount_override is now supported in items/add, items/update and proration-preview, and unsupported fields now return a 400 error instead of being ignored. See Change log.
V2 supports prorated changes to the items of an active contract:
Path |
Description |
|---|---|
|
Previews the impact of a change without executing it. |
|
Adds a new item to the contract. |
|
Updates an item’s price, quantity, discount or fixed unit amount. |
|
Removes an item from the contract. |
Main request fields:
Action |
Fields |
Required |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
In items/update you only need to send the fields you want to change; omitted fields keep the item’s current values. If the request contains no actual change, a 400 response with code set to no_change is returned.
Unknown fields in requests to these endpoints return a 400 error instead of being silently ignored. The same applies to fields not supported by the selected operation, for example unit_amount_override in items/remove.
Fixed item price (unit_amount_override)
unit_amount_override sets a fixed per-unit amount for an item instead of the catalog price. The field is supported at contract creation and also in items/add, items/update and proration-preview.
In items/update the following applies:
If the field is omitted, the item’s current fixed price is kept unchanged.
If an explicit
nullis sent, the fixed price is removed and the item reverts to the catalog price.Changing only the fixed price counts as a valid change and does not return
no_change.
Proration lines created when only the fixed price changes get the proration_reason value unit_amount_changed.
Example of setting a fixed price on an item:
curl -X POST https://askell.is/api/v2/subscription-contracts/123/items/update/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"contract_item": 456,
"unit_amount_override": "8000.0000",
"settlement_behavior": "invoice_now",
"idempotency_key": "item-456-fast-verd"
}'
The same preview pattern applies as for lifecycle operations: first call proration-preview with the same fields, store the preview_token from the response and send it with the execution call.
Lifecycle actions
V2 supports the following lifecycle calls on contracts:
Path |
Description |
|---|---|
|
Cancels the contract, either immediately or at the end of the period. |
|
Restarts a contract that has been stopped. |
|
Temporarily pauses a contract. |
|
Resumes a paused contract. |
|
Deletes a scheduled pause. |
|
Activates an inactive contract if it can move to the active state. |
Main request fields:
Action |
Fields |
Required |
|---|---|---|
|
|
All optional |
|
|
All optional |
|
|
|
|
|
All optional |
Lifecycle operations that can affect pricing within an active service period support the same proration pattern as item changes:
Call
POST /api/v2/subscription-contracts/{id}/proration-preview/withoperationset tocancel,pause,resumeorrestart.Store the
preview_tokenfrom the response if the same operation is to be executed right away.Send the same
preview_tokenback in the execution call along with the same settings forproration_behaviorandsettlement_behavior.
include_pending_adjustments=true is only valid when settlement_behavior=invoice_now and must then be sent in both the preview and the execution.
proration_behavior can be none, create_prorations, or always_invoice. settlement_behavior can be next_invoice, invoice_now, or credit_balance. refund_manual and operations for one-time refunds or manual adjustments are not part of the external V2 API flow at this time.
Contract credit
A prorated credit, for example from cancellation, pause, or item changes, can create a credit transaction on the contract. This credit is recorded as immutable transaction history on the contract.
The current rule for the future implementation is that credit is applied after taxes and line totals have been calculated, but before attempting to charge the payment processor. Credit can be used partially or fully, and if credit covers the entire billing run, no processor charge is attempted. Credit created by the same billing run cannot be used by that same run.
Automatic use of credit in the next billing run is not yet part of the external API flow. Until then, credit is recorded and visible in the dashboard/history, but it does not automatically reduce the next charge.
Example of canceling a contract at the end of the period:
curl -X POST https://askell.is/api/v2/subscription-contracts/123/cancel/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"cancel_at_period_end": true,
"reason": "Viðskiptavinur óskaði eftir lokun"
}'
See Validity and cancellation for how a cancellation is reflected in cancel_at_period_end, cancel_at, canceled_at and ended_at after the call has been applied.
Example of previewing a prorated restart:
curl -X POST https://askell.is/api/v2/subscription-contracts/123/proration-preview/ \
-H "Authorization: Api-Key your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"operation": "restart",
"proration_behavior": "create_prorations",
"settlement_behavior": "invoice_now"
}'
Checkout flow
When payment pre-processing is required before a contract becomes active, the checkout flow is recommended:
Calculate or confirm the quote.
Fetch eligible payment processors with
POST /api/v2/payment-processor-options/.Create a
checkoutwithPOST /api/v2/checkouts/.Finalize the
checkoutwithPOST /api/v2/checkouts/{token}/finalize/.
POST /api/v2/payment-processor-options/ returns the payment processors that are valid for the given quote, currency, and collection method. If only one processor is available, it can be used as the default. If more than one is eligible, the integration should allow a choice.
A shortened response might look like this:
{
"currency": "ISK",
"collection_method": "card",
"selected_account_payment_processor_id": 7,
"selection_reason": "single_eligible",
"requires_selection": false,
"results": [
{
"account_payment_processor_id": 7,
"display_name": "Straumur / Adyen",
"payment_processor": "adyen",
"collection_method": "card",
"resolution_source": "eligible",
"supports_initial_charge": true,
"supports_recurring_charge": true,
"render_mode": "adyen_checkout",
"payment_processor_type": "adyen",
"is_3d_secure": true,
"card_collection_in_frontend": true,
"supports_checkout": true,
"address_required": false,
"registration_mode": "delayed_tokenization",
"public_registration_config": {}
}
]
}
render_mode tells the frontend how card details are collected: generic_card_form (card form hosted by Áskell), verifone_encrypted_card (encryption in the browser), adyen_checkout (Straumur/Adyen drop-in) or teya_checkout (Teya Embedded Checkout). Both adyen_checkout and teya_checkout use delayed-tokenization: the card is stored with the payment processor after the payment page has completed the checkout and the payment session is finalized with a webhook (or a synchronous confirmation with Teya).
POST /api/v2/checkouts/ creates a checkout object that stores the quote snapshot, customer, selected payment processor, and other prerequisite data.
GET /api/v2/checkouts/{token}/ fetches the status of the checkout object. checkout_url points to this endpoint.
POST /api/v2/checkouts/{token}/finalize/ attempts to complete contract creation and the initial billing.
Prerequisites before calling finalize
finalize now performs a preflight check before creating the contract. The following must therefore be valid before attempting finalize:
The customer must exist.
The selected payment processor must be valid for the quote.
The customer must have a verified payment method that matches the selected payment processor, unless the initial billing amount is 0 ISK.
If these prerequisites are not met, an error response is returned and no contract or billing run is created.
It is important to distinguish between two kinds of 400 responses from finalize:
Precondition failure: no contract and no billing run are created.
Failed payment attempt: the contract and billing run may already have been created, but
checkout.statusbecomesfailed.
A shortened finalize response might look like this:
{
"id": 15,
"token": "f2dd7db2-4ae1-45c0-b8f6-2f98d1b70a9a",
"checkout_url": "https://askell.is/api/v2/checkouts/f2dd7db2-4ae1-45c0-b8f6-2f98d1b70a9a/",
"status": "succeeded",
"customer_id": 1008,
"customer_reference": "customer-123",
"currency": "ISK",
"subtotal_amount": "4500.0000",
"tax_amount": "0.0000",
"total_amount": "4500.0000",
"contract_id": 33,
"initial_billing_run_id": 32,
"account_payment_processor_id": 7,
"quote_snapshot": {
"input_mode": "bundle",
"total_amount": "4500.0000"
}
}
Outcomes from finalize
Status |
HTTP |
Description |
Next step |
|---|---|---|---|
|
|
The contract has been created and the initial billing has completed. |
Store |
|
|
An external processor must complete its flow before the billing is considered complete. |
Monitor the |
|
|
The payment attempt failed after the contract and billing run were created. |
Inspect |
Precondition failure |
|
The request data or payment prerequisites were invalid; no contract was created. |
Correct the request data or payment method before trying again. |
Billing runs and retries
V2 billing runs are read from:
GET /api/v2/billing-runs/
GET /api/v2/billing-runs/{id}/
A failed billing run can be retried with the following endpoint:
POST /api/v2/billing-runs/{id}/retry/
The billing run detail endpoint response includes, among other things, lines, attempts, related transactions, and status.
A shortened response might look like this:
{
"id": 32,
"contract_id": 33,
"customer_id": 1008,
"customer_reference": "customer-123",
"period_start_at": "2026-05-20T00:00:00Z",
"period_end_at": "2026-06-20T00:00:00Z",
"state": "succeeded",
"currency": "ISK",
"subtotal_amount": "4500.0000",
"tax_amount": "0.0000",
"total_amount": "4500.0000",
"attempt_count": 1,
"lines": [
{
"id": 71,
"price_id": 200,
"price_version_id": 15,
"product_name": "Vefáskrift",
"quantity": 2,
"line_total_amount": "4000.0000",
"service_period_start_at": "2026-05-20T00:00:00Z",
"service_period_end_at": "2026-06-20T00:00:00Z"
}
],
"attempts": [
{
"id": 44,
"attempt_no": 1,
"state": "succeeded",
"transaction_id": 19,
"fail_code": null,
"fail_message": null
}
]
}
Versioning policy and rate limits
V2 is versioned by URL, that is, under /api/v2/. New breaking changes should therefore appear under a new major-version path rather than silently changing the meaning of existing endpoints.
No documented rate limits are defined on this page. Integrations should still expect transient failures, use retries with backoff, and log responses with status codes such as 400, 404, and 5xx.
Example end-to-end flow in Python
import requests
API_KEY = 'your api key here'
headers = {
"Authorization": f"Api-Key {API_KEY}",
"Content-Type": "application/json",
}
quote_payload = {
"customer_reference": "customer-123",
"currency": "ISK",
"bundle_template": 42,
"bundle_item_selections": [
{
"bundle_item": 100,
"selected_price": 200,
}
],
"initial_items": [
{
"price": 500,
"quantity": 1,
}
],
}
try:
quote_response = requests.post(
"https://askell.is/api/v2/subscription-offer-quotes/",
json=quote_payload,
headers=headers,
)
quote_response.raise_for_status()
processor_response = requests.post(
"https://askell.is/api/v2/payment-processor-options/",
json=quote_payload,
headers=headers,
)
processor_response.raise_for_status()
processor_options = processor_response.json()["results"]
# Hér er gert ráð fyrir að aðeins einn færsluhirðir komi til greina.
# Ef fleiri en einn er í boði þarf samþættingin að leyfa val.
checkout_payload = {
**quote_payload,
"collection_method": "card",
"account_payment_processor": processor_options[0]["account_payment_processor_id"],
}
checkout_response = requests.post(
"https://askell.is/api/v2/checkouts/",
json=checkout_payload,
headers=headers,
)
checkout_response.raise_for_status()
checkout = checkout_response.json()
finalize_response = requests.post(
f"https://askell.is/api/v2/checkouts/{checkout['token']}/finalize/",
json={},
headers=headers,
)
finalized_checkout = finalize_response.json()
if finalize_response.status_code == 400:
contract_id = finalized_checkout.get("contract_id")
initial_billing_run_id = finalized_checkout.get("initial_billing_run_id")
if contract_id:
# Greiðslutilraun mistókst, en samningur og innheimtulota gætu þegar verið til.
# Hér ætti raunveruleg samþætting að skrá þetta og meta hvort reyna eigi aftur.
print(
"Checkout failed after contract creation",
contract_id,
initial_billing_run_id,
)
else:
# Forsendubrestur: enginn samningur var stofnaður.
finalize_response.raise_for_status()
else:
finalize_response.raise_for_status()
except requests.HTTPError as exc:
response = exc.response
try:
error_body = response.json()
except ValueError:
error_body = {"raw": response.text}
print(response.status_code, error_body)
raise
Common errors
Error |
HTTP |
Description |
|---|---|---|
|
|
The customer was not found or is missing from the request data. |
|
|
The selected payment processor is not valid for the quote. |
|
|
No verified payment method was found for the customer. |
|
|
A required price selection is missing or does not match the bundle. |
|
|
The selected add-on product does not match an active add-on rule. |
|
|
The direct price selection is invalid or does not match the currency. |
Not found |
|
The contract, |