Webhooks
Webhooks call a URL that you register in Áskell when certain events happen. These are the events you can subscribe to:
Event identifier |
Description |
|---|---|
|
All events that are connected to subscriptions |
|
Subscription created |
|
Subscription changed |
|
Subscription renewed |
|
All events related to subscription contracts |
|
Subscription contract created |
|
Subscription contract changed |
|
Subscription contract renewed |
|
Legacy subscription migrated to a subscription contract |
|
Subscription contract activated |
|
Subscription contract paused |
|
Subscription contract resumed after a pause |
|
Subscription contract canceled immediately |
|
Subscription contract ended |
|
All events related to contract items |
|
Contract item entitlements changed (not covered by |
|
All events related to scheduled contract item changes (not covered by |
|
Item change scheduled for the next renewal |
|
Scheduled item change applied at renewal |
|
Scheduled item change canceled |
|
All events related to billing runs |
|
Billing run created |
|
Billing run changed |
|
Billing run succeeded |
|
Billing run failed permanently |
|
Billing run retry scheduled |
|
All events related to fulfillment orders |
|
Fulfillment order created |
|
Fulfillment order fulfilled |
|
Fulfillment order cancelled |
|
Shipment booked with the carrier (includes the tracking number) |
|
All events that are connected to customers |
|
Customer created |
|
Customer changed |
|
All events related to payments (individual payments and subscription contract charges) |
|
Payment created |
|
Payment changed (e.g. the charge succeeds or fails) |
|
Payment retry scheduled |
|
All events that are connected to checkouts |
|
Checkout created |
|
Checkout changed |
Identification
We recommend that all webhooks are authenticated. We sign each call with a header named Hook-HMAC. Its value is an HMAC digest built from the body of the call and a secret known only to us and you. You receive this secret when you create a new webhook. The event type is also sent in the Hook-Event header. The API data version is sent in the Hook-API-Version header. Legacy plan/subscription webhooks send v1, while the new subscription contract system sends v2.
Here is a Python example. The process is very similar to what Shopify uses and you can find more code examples in their documentation here.
import base64
import hmac
import hashlib
WEBHOOK_SECRET = "Your webhook secret".encode()
WEBHOOK_DIGEST_TYPE = 'sha512'
def verify(hmac_header, digest_method, secret, message):
digestmod = getattr(hashlib, digest_method)
signed = base64.b64encode(
hmac.new(secret, message, digestmod).digest(),
).strip()
return hmac.compare_digest(signed, hmac_header)
# your view function
def handle_webhook(request):
# The signature
digest = request.META.get('HTTP_HOOK_HMAC').encode()
# The name of the webhook event
event = request.META.get('HTTP_HOOK_EVENT').encode()
body = request.body
if verify(digest, WEBHOOK_DIGEST_TYPE, WEBHOOK_SECRET, body):
payload = json.loads(body)
# ... the rest of your code here```
require 'rubygems'
require 'base64'
require 'openssl'
require 'sinatra'
WEBHOOK_SECRET = 'Your webhook secret'
WEBHOOK_DIGEST_TYPE = 'sha512'
helpers do
def verify_webhook(secret, data, hmac_header)
digested = ::OpenSSL::HMAC.digest(WEBHOOK_DIGEST_TYPE, secret, data)
calculated_hmac = Base64.encode64(digested).strip
return calculated_hmac == hmac_header
end
end
post '/' do
request.body.rewind
data = request.body.read
if verify_webhook(WEBHOOK_SECRET, env["HTTP_HOOK_HMAC"])
# deserialize data' using json and process webhook
end
end
<?php
define('WEBHOOK_SECRET', 'Your webhook secret')
define('WEBHOOK_DIGEST_TYPE', 'sha512')
function verify_webhook($data, $hmac_header)
{
$calculated_hmac = base64_encode(hash_hmac(WEBHOOK_DIGEST_TYPE, $data,
WEBHOOK_SECRET, true));
return ($hmac_header == $calculated_hmac);
}
$hmac_header = $_SERVER['HTTP_HOOK_HMAC'];
$data = file_get_contents('php://input');
$verified = verify_webhook($data, $hmac_header);
?>
Subscription contracts and migration from the legacy subscription system
V2 subscription contracts use subscription_contract.* events. When a legacy subscription is migrated to V2, a subscription_contract.migrated event is sent. It means that the V2 contract is the new billing and access authority for the relevant subscription.
During migration, legacy subscription.* aliases are not sent for the V2 contract because the event data is based on SubscriptionContract, not the legacy Subscription object. For the same reason, a subscription.canceled event is not sent solely because billing moves from a legacy subscription to a V2 contract.
Data in webhooks
The body of every call is a JSON object with four fields:
{
"event": "subscription_contract.created",
"ref": null,
"sender": "notandanafn",
"data": {}
}
event is the name of the event (the same value as in the Hook-Event header), sender is the username of the account that sends the event, and ref is unused and always null. The payload itself is in data. The examples below show the contents of data for each group of events.
Subscriptions (subscription.*)
When a subscription is created, updated or renewed the following events are sent. Here is an example of data that comes with a subscription event:
{
"id": 12345,
"plan": {
"id": 12345,
"name": "Titill",
"alternative_name": "Annar titill",
"reference": "",
"interval": "month",
"interval_count": 1,
"amount": "1.0000",
"currency": "ISK",
"trial_period_days": 5,
"enabled": true,
"private": false,
"electronic_only": true,
"description": "Some description"
},
"customer": 12345,
"customer_reference": "customer-12345",
"trial_end": "2024-01-01T00:00:00Z",
"start_date": "2024-01-01T00:00:00Z",
"ended_at": null,
"reference": "reference-12345",
"active": true,
"meta": "{}",
"description": "Some subscription description",
"active_until": "2024-02-01T00:00:00Z",
"is_on_trial": false,
"token": "md5sumtokenwith32chars",
"is_failing": false,
"last_billing_log": {
"id": 12345,
"billing_log_type": "renewal",
"billing_date": "2024-01-01T00:00:00Z",
"plan_billed_up_to": "2024-02-01T00:00:00Z",
"total": null,
"transaction": {
"external_reference": null,
"data": {},
"state": "settled",
"uuid": "some-guid-with-numbers-and-letters",
"fail_code": null,
"refund_code": null,
"cancel_code": null,
"amount": "1.00",
"currency": "ISK",
"payment_method": null,
"created_at": "2024-01-01T00:00:00Z"
}
},
"delivery_address": null,
"amount": "1.0000"
}
Subscription contracts (subscription_contract.*)
The events are sent in the following situations:
Event |
When |
|---|---|
|
A subscription contract is created. |
|
A subscription contract is saved with changes, e.g. when a cancellation is scheduled for the end of the period. |
|
Billing for a new period has succeeded and the subscription contract moves on to it. |
|
A legacy subscription has been migrated to a subscription contract. See the payload below. |
|
A subscription contract becomes active, e.g. when the first payment succeeds or when a canceled subscription contract is restarted. |
|
A pause takes effect. |
|
A pause ends and the subscription contract is active again. |
|
A subscription contract is canceled immediately. |
|
A subscription contract ends, either because a scheduled cancellation takes effect or because a one-time purchase contract concludes. |
subscription_contract.changed is sent every time a subscription contract is saved, so it often arrives alongside other events in the table. Note that when a scheduled cancellation takes effect, subscription_contract.ended is sent, not subscription_contract.canceled, even though the subscription contract’s state becomes canceled. subscription_contract.canceled is only sent when a subscription contract is canceled immediately.
Events about contract item entitlements (subscription_contract_item.*) and scheduled item changes (subscription_contract_scheduled_change.*) do not send the subscription contract and are therefore not covered by subscription_contract.*. A webhook has to be registered for them specifically, see Contract item entitlements (subscription_contract_item.*) and Scheduled item changes (subscription_contract_scheduled_change.*).
All subscription_contract.* events except subscription_contract.migrated send the subscription contract in the same shape as the V2 API returns it, see Fields in the contract response. Here is an example of the data sent with a subscription contract event:
{
"id": 12345,
"customer_id": 33482,
"customer": {
"id": 33482,
"customer_reference": "customer-12345",
"email": "jon@example.com",
"customer_username": "jon"
},
"customer_reference": "customer-12345",
"reference": null,
"delivery_address": null,
"shipping_selection": null,
"state": "active",
"billing_anchor_at": "2024-01-01T00:00:00Z",
"billing_timezone": "Atlantic/Reykjavik",
"billing_time": "00:00:00",
"billing_advance_policy": "on_run_succeeded",
"service_entitlement_policy": "on_run_succeeded",
"service_active": true,
"service_state": "active",
"active_service_item_count": 1,
"inactive_service_item_count": 0,
"total_active_quantity": 1,
"entitlement_summary": {
"service_active": true,
"service_state": "active",
"active_service_item_count": 1,
"inactive_service_item_count": 0,
"past_due_service_item_count": 0,
"total_active_quantity": 1,
"active_product_ids": [
222
],
"evaluated_at": "2024-01-01T00:00:00Z"
},
"billing_attention_required": false,
"trial_start_at": null,
"trial_end_at": null,
"next_billing_at": "2024-02-01T00:00:00Z",
"cancel_at": null,
"cancel_at_period_end": false,
"canceled_at": null,
"ended_at": null,
"currency": "ISK",
"recurring": true,
"contract_version": 1,
"default_proration_behavior": "create_prorations",
"electronic_only": true,
"has_future_pause": false,
"paused_until": null,
"payment_processor_override_id": null,
"accounting_department": null,
"accounting_cost_center": null,
"legacy_subscription_ids": [
9876
],
"legacy_source_mappings": [
{
"legacy_subscription_id": 9876,
"subscription_contract_item_id": 23456,
"role": "item_source",
"migration_effective_at": "2024-01-01T00:00:00Z"
}
],
"metadata": {},
"subscriber_page": "https://askell.is/change_contract/<token>/",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"items": [
{
"id": 23456,
"price_id": 333,
"product": {
"id": 222,
"name": "Mánaðaráskrift",
"reference": "manadaraskrift",
"image_url": null
},
"price": {
"id": 333,
"product_id": 222,
"product_name": "Mánaðaráskrift",
"product_reference": "manadaraskrift",
"currency": "ISK",
"currency_format": "{amount} kr",
"unit_amount": "2990.0000",
"description": "",
"billing_type": "recurring",
"recurrence_type": "interval",
"interval": "month",
"interval_count": 1,
"billing_day_of_month": null,
"billing_schedule_id": null,
"billing_schedule_name": null,
"billing_schedule_timezone": null,
"billing_schedule_rules": [],
"proration_policy": "prorate_by_time",
"initial_billing_policy": "next_scheduled_period",
"next_occurrences": [],
"active": true,
"current_version_id": 444,
"versions": [
{
"id": 444,
"unit_amount": "2990.0000",
"valid_from": null,
"valid_to": null,
"active": true,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"currently_effective": true,
"effective_from": null,
"effective_to": null,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"quantity": 1,
"unit_amount_override": null,
"discount_percent": null,
"active": true,
"start_at": "2024-01-01T00:00:00Z",
"end_at": null,
"billing_anchor_at": "2024-01-01T00:00:00Z",
"next_billing_date": null,
"next_billing_at": "2024-02-01T00:00:00Z",
"last_billed_at": "2024-01-01T00:00:00Z",
"service_active": true,
"service_state": "active",
"entitled_from": "2024-01-01T00:00:00Z",
"entitled_until": "2024-02-01T00:00:00Z",
"current_service_period_start_at": "2024-01-01T00:00:00Z",
"current_service_period_end_at": "2024-02-01T00:00:00Z",
"entitlement_billing_run_id": 54321,
"metadata": {}
}
],
"pauses": [],
"current_pauses": [],
"future_pauses": [],
"past_pauses": [],
"billing_cadence": {
"recurrence_type": "interval",
"interval": "month",
"interval_count": 1,
"billing_day_of_month": null,
"billing_schedule_id": null,
"billing_schedule_name": null
},
"bundle_purchases": [],
"bundle_purchase_id": null,
"initial_billing_run": {
"id": 54321,
"state": "succeeded",
"run_type": "recurring",
"period_start_at": "2024-01-01T00:00:00Z",
"period_end_at": "2024-02-01T00:00:00Z",
"billing_date": "2024-01-01",
"due_at": "2024-01-01T00:00:00Z",
"currency": "ISK",
"subtotal_amount": "2990.0000",
"tax_amount": "0.0000",
"total_amount": "2990.0000",
"attempt_count": 1,
"max_attempts": 5,
"next_retry_at": null,
"last_attempt_at": "2024-01-01T00:00:00Z",
"transaction_id": 98765,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"initial_billing_run_id": 54321,
"initial_billing_run_total_amount": "2990.0000",
"latest_billing_run": {
"id": 54321,
"state": "succeeded",
"run_type": "recurring",
"period_start_at": "2024-01-01T00:00:00Z",
"period_end_at": "2024-02-01T00:00:00Z",
"billing_date": "2024-01-01",
"due_at": "2024-01-01T00:00:00Z",
"currency": "ISK",
"subtotal_amount": "2990.0000",
"tax_amount": "0.0000",
"total_amount": "2990.0000",
"attempt_count": 1,
"max_attempts": 5,
"next_retry_at": null,
"last_attempt_at": "2024-01-01T00:00:00Z",
"transaction_id": 98765,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"discount": null
}
customer is an object with details about the customer, while the customer’s ID is in customer_id. service_active and service_state tell whether service should be provided right now.
reference is the subscription contract’s reference in an external system and metadata the integration’s metadata, including metadata from a checkout and where the subscription contract came from in askell_source. Both are set when the subscription contract is created, so they are already included in subscription_contract.created. See Contract reference and metadata.
legacy_subscription_ids and legacy_source_mappings link the subscription contract to the legacy subscriptions that were migrated to it. Both lists are empty if no legacy subscription was migrated. Each entry in legacy_source_mappings tells which legacy subscription (legacy_subscription_id) became which contract item (subscription_contract_item_id), its role in the migration (role: primary, item_source or history_only) and when the migration took effect (migration_effective_at).
Migration from the legacy subscription system (subscription_contract.migrated)
subscription_contract.migrated does not send the whole subscription contract but a shorter payload about the migration:
{
"id": 12345,
"subscription_contract_id": 12345,
"migration_batch_id": 77,
"customer": 33482,
"customer_reference": "customer-12345",
"billing_managed_by": "subscription_contract",
"migration_effective_at": "2024-01-01T00:00:00+00:00",
"legacy_subscription_ids": [
9876
],
"legacy_source_mappings": [
{
"legacy_subscription_id": 9876,
"subscription_contract_item_id": 23456,
"role": "item_source",
"migration_effective_at": "2024-01-01T00:00:00+00:00"
}
]
}
Field |
Description |
|---|---|
|
ID of the subscription contract. Both fields have the same value. |
|
ID of the migration batch that migrated the subscription. |
|
ID of the customer as a number. Unlike the other |
|
Customer reference. |
|
Always |
|
The earliest effective time in |
|
IDs of the legacy subscriptions that were migrated to the subscription contract. |
|
Same shape as in the other |
Billing runs (billing_run.*)
When a billing run is created, changes, succeeds, fails or is scheduled for a retry, the corresponding events are sent. Here is an example of the data sent with a billing run event:
{
"id": 54321,
"contract_id": 12345,
"source_contract_change_id": null,
"run_type": "recurring",
"customer_id": 33482,
"customer_reference": "customer-12345",
"period_start_at": "2024-01-01T00:00:00Z",
"period_end_at": "2024-02-01T00:00:00Z",
"billing_date": "2024-01-01",
"due_at": "2024-01-01T00:00:00Z",
"state": "succeeded",
"currency": "ISK",
"subtotal_amount": "2990.0000",
"tax_amount": "0.0000",
"total_amount": "2990.0000",
"attempt_count": 1,
"max_attempts": 5,
"next_retry_at": null,
"last_attempt_at": "2024-01-01T00:00:00Z",
"transaction_id": 98765,
"transaction_uuid": "3f1c2a9e-5b7d-4c8e-9a6f-2d4b8e1c7a90",
"transaction_external_reference": "410009982476",
"lines": [
{
"id": 76543,
"contract_id": 12345,
"line_type": "recurring",
"contract_item_id": 23456,
"price_id": 333,
"price_version_id": null,
"product_name": "Mánaðaráskrift",
"product_reference": "manadaraskrift",
"quantity": 1,
"unit_amount": "2990.0000",
"discount_percent": null,
"currency": "ISK",
"line_subtotal_amount": "2990.0000",
"line_tax_amount": "0.0000",
"line_total_amount": "2990.0000",
"service_period_start_at": "2024-01-01T00:00:00Z",
"service_period_end_at": "2024-02-01T00:00:00Z",
"proration_factor": "1.000000",
"proration_reason": null,
"proration_behavior": null,
"settlement_behavior": null,
"change_effective_at": null,
"adjustment_service_start_at": null,
"adjustment_service_end_at": null,
"source_billing_run_line_id": null,
"source_contract_change_id": null,
"imported_billing_run_id": null,
"target_contract_item_id": null,
"target_billing_date": null,
"target_due_at": null,
"tax_rate_id": null,
"tax_rate_version_id": null,
"tax_percent": null,
"tax_calculation_mode": null,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"attempts": [
{
"id": 65432,
"attempt_no": 1,
"state": "succeeded",
"idempotency_key": "v2-billing-run-54321-attempt-1",
"transaction_id": 98765,
"transaction_uuid": "3f1c2a9e-5b7d-4c8e-9a6f-2d4b8e1c7a90",
"transaction_external_reference": "410009982476",
"fail_code": null,
"fail_message": null,
"started_at": "2024-01-01T00:00:00Z",
"finished_at": "2024-01-01T00:00:00Z",
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
contract_id is the ID of the subscription contract. transaction_id, transaction_uuid and transaction_external_reference point to the transaction that charged the billing run and are null if no transaction has been created. lines are the lines of the billing run and attempts the charge attempts. state is scheduled, processing, retry_scheduled, pending_external, succeeded, failed_terminal, canceled, voided or refunded.
Contract item entitlements (subscription_contract_item.*)
subscription_contract_item.entitlement_changed is sent when a contract item’s entitlements change: whether service should be provided (service_active / service_state), until when (entitled_until), the quantity or the item’s product. This happens, for example, when a billing run succeeds and grants entitlements for a new period, when a trial starts, when a subscription contract is paused, resumed, canceled or ends, and when a contract item is changed.
Important
This event is not covered by subscription_contract.*. To receive it, register a webhook specifically for subscription_contract_item.entitlement_changed or subscription_contract_item.*.
{
"id": 23456,
"subscription_contract_id": 12345,
"contract_item_id": 23456,
"customer": 33482,
"customer_reference": "customer-12345",
"product_id": 222,
"product_reference": "manadaraskrift",
"price_id": 333,
"quantity": 1,
"service_active": true,
"service_state": "active",
"entitled_from": "2024-01-01T00:00:00+00:00",
"entitled_until": "2024-02-01T00:00:00+00:00",
"current_service_period_start_at": "2024-01-01T00:00:00+00:00",
"current_service_period_end_at": "2024-02-01T00:00:00+00:00",
"entitlement_billing_run_id": 54321,
"changed_fields": [
"entitled_until",
"service_active",
"service_state"
],
"previous": {
"service_active": false,
"service_state": "inactive",
"entitled_until": null,
"quantity": 1,
"product_id": 222,
"product_reference": "manadaraskrift"
}
}
id and contract_item_id are the ID of the contract item. changed_fields lists the fields that changed, and previous holds the entitlement fields (service_active, service_state, entitled_until, quantity, product_id and product_reference) as they were before the change. entitlement_billing_run_id is the billing run that granted the entitlements, if any.
Scheduled item changes (subscription_contract_scheduled_change.*)
These events follow item changes that are scheduled for the next renewal (apply_at=period_end), see Scheduled item changes (apply_at=period_end). They are sent in the following situations:
Event |
When |
|---|---|
|
An item change is scheduled for the next renewal. |
|
The billing run for the renewal is created and the change has been applied to the item. |
|
A scheduled change is canceled, or is canceled automatically because the item is removed or the subscription contract is canceled. |
Important
These events are not covered by subscription_contract.*. To receive them, register a webhook specifically for the individual events or for subscription_contract_scheduled_change.*.
The payload is the scheduled change in the same shape as it appears in scheduled_changes in the V2 API, together with the IDs of the subscription contract and the customer. Here is an example of the data sent with subscription_contract_scheduled_change.created:
{
"id": 8765,
"contract_item": 23456,
"operation": "update_item",
"state": "scheduled",
"scheduled_for": "2024-02-01T00:00:00Z",
"price_id": 334,
"price": {
"id": 334,
"product_id": 223,
"product_name": "Mánaðaráskrift Plus",
"product_reference": "manadaraskrift-plus",
"currency": "ISK",
"currency_format": "{amount} kr",
"unit_amount": "1990.0000",
"description": "",
"billing_type": "recurring",
"recurrence_type": "interval",
"interval": "month",
"interval_count": 1,
"billing_day_of_month": null,
"billing_schedule_id": null,
"billing_schedule_name": null,
"billing_schedule_timezone": null,
"billing_schedule_rules": [],
"proration_policy": "prorate_by_time",
"initial_billing_policy": "next_scheduled_period",
"next_occurrences": [],
"active": true,
"current_version_id": 445,
"versions": [
{
"id": 445,
"unit_amount": "1990.0000",
"valid_from": null,
"valid_to": null,
"active": true,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"currently_effective": true,
"effective_from": null,
"effective_to": null,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"product": {
"id": 223,
"name": "Mánaðaráskrift Plus",
"reference": "manadaraskrift-plus"
},
"quantity": 1,
"discount_percent": null,
"unit_amount_override": null,
"from_price_id": 333,
"from_quantity": 1,
"reason": null,
"applied_at": null,
"canceled_at": null,
"applied_change_id": null,
"billing_run_id": null,
"created_at": "2024-01-15T12:00:00Z",
"updated_at": "2024-01-15T12:00:00Z",
"subscription_contract_id": 12345,
"customer": 33482,
"customer_reference": "customer-12345"
}
Field |
Description |
|---|---|
|
ID of the scheduled change, not of the subscription contract. |
|
The subscription contract the change belongs to. |
|
ID of the contract item that changes. |
|
Always |
|
|
|
The renewal at which the change takes effect. |
|
The price the item switches to at the renewal, in the same shape as |
|
The quantity, discount and unit amount override the item switches to at the renewal. |
|
The item’s price and quantity when the change was scheduled. |
|
Optional reason given when the change was scheduled. |
|
When the change was applied, the change recorded in the subscription contract’s history and the billing run for the renewal. |
|
When the change was canceled. |
|
When the scheduled change was created and last updated. |
|
ID of the customer as a number, and the customer reference. |
Pending changes are also included with the subscription contract itself, in scheduled_changes and in scheduled_change on the item concerned, in the subscription_contract.* events that send the subscription contract.
Fulfillment orders (fulfillment_order.*)
When a physical product is paid for, a fulfillment order is created that the seller has to pick, pack and ship. The events follow it through the process: fulfillment_order.created when it is created (after the payment has succeeded), fulfillment_order.shipment_booked when a shipment is booked with the carrier and a tracking number is available, fulfillment_order.fulfilled when the order counts as fulfilled and fulfillment_order.cancelled if it is cancelled.
All four carry the same payload, i.e. the whole order as it stands when the event is sent. fulfillment_order.shipment_booked additionally includes shipment_id, which points to the shipment in fulfillments that was being booked.
{
"id": 12345,
"number": 42,
"status": "open",
"contract_id": 12345,
"billing_run_id": 54321,
"customer_id": 33482,
"customer_reference": "customer-12345",
"customer_name": "Jón Jónsson",
"customer_email": "jon@example.com",
"delivery_address": {
"id": 9876,
"delivery_name": "Jón Jónsson",
"address_1": "Hringbraut 12",
"address_2": "",
"address_3": "",
"zip_code": "101",
"city": "Reykjavík",
"state": null,
"country": "IS"
},
"shipping_selection": {
"id": 555,
"option_id": 7,
"handler": "posturinn",
"delivery_mode": "pickup_point",
"option_name": "Póstbox",
"service_code": "DPO",
"price_amount": "940.0000",
"currency": "ISK",
"location_id": "9591",
"location_name": "Póstbox Hallveigarstíg",
"location_address": "Hallveigarstíg 1 101 Reykjavík",
"location": {
"id": "9591",
"name": "Póstbox Hallveigarstíg",
"address": "Hallveigarstíg 1 101 Reykjavík",
"street": "Hallveigarstíg 1",
"zip_code": "101",
"town": "Reykjavík",
"external_id": null,
"latitude": 64.1,
"longitude": -21.9
}
},
"lines": [
{
"id": 1111,
"product_id": 222,
"product_reference": "SKU-1",
"product_name": "Kaffipoki",
"quantity": 2,
"quantity_fulfilled": 0
}
],
"fulfillments": [
{
"id": 333,
"order_id": 12345,
"handler": "posturinn",
"status": "booked",
"provider_order_id": "PP123456789",
"tracking_number": "PP123456789",
"weight_grams": 1000,
"error": "",
"booked_at": "2024-01-01T00:00:00Z",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"estimated_weight_grams": 1000,
"metadata": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
A seller who sells physical products but has no carrier set up receives orders without a shipping selection: shipping_selection is then null and fulfillments an empty list. The fulfillment_order.created and fulfillment_order.fulfilled events are still sent — the order has to be picked and fulfilled whether or not Áskell books the shipment.
Customer (customer.*)
When a customer is created or updated the following events are sent. Here is an example of data that comes with a customer event:
{
"id": 33482,
"first_name": "",
"last_name": "",
"delivery_name": "",
"address_1": "",
"address_2": "",
"address_3": "",
"zip_code": "",
"city": "",
"country": "",
"email": null,
"customer_reference": "0208912039",
"payment_method": [],
"address": null
}
Payments (payment.*)
payment.* events arrive with one of two payloads, depending on where the payment comes from:
Individual payments created through Individual payments send the legacy shape, with a list of transactions in
transactions. TheHook-API-Versionheader is thenv1.Subscription contract charges, i.e. payments created by a billing run, send a flat shape that describes a single transaction directly in the payload. The
Hook-API-Versionheader is thenv2.
A webhook registered for payment.*, or for an individual payment event, receives both. To tell them apart, read the Hook-API-Version header, or check whether the subscription_contract_id field is in the payload. It is only present in the shape for subscription contract charges.
Individual payments (Hook-API-Version: v1)
When an individual payment is created, changed or retried, the corresponding events are sent. Here is an example of the data sent with an individual payment event:
{
"uuid": "some-guid-with-numbers-and-letters",
"amount": "1.0000",
"currency": "ISK",
"description": "Description here",
"reference": "some-reference",
"state": "settled",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"transactions": [
{
"external_reference": "410009982476",
"data": {
"id": "4100000000",
"receipt": {}, // receipt contains vendor specific fields, not listed here
"status": "settled"
},
"state": "settled",
"uuid": "some-guid-with-numbers-and-letters",
"fail_code": null,
"refund_code": null,
"cancel_code": null,
"amount": "1.00",
"currency": "ISK",
"payment_method": {
"verified": true,
"canceled": false,
"valid_until": "2025-03-01T00:00:00Z",
"display_info": "XXXX-XXXX-XXXX-4324 (Visa)",
"credit_card": true
},
"created_at": "2024-01-01T00:00:00Z"
}
]
}
Subscription contract charges (Hook-API-Version: v2)
A billing run sends payment.created when a transaction is created for a charge attempt, payment.changed when the charge succeeds, fails permanently or awaits confirmation from the payment processor, and payment.retry when a new attempt is scheduled. Here is an example of the data sent with a subscription contract charge event:
{
"uuid": "3f1c2a9e-5b7d-4c8e-9a6f-2d4b8e1c7a90",
"amount": "2990.00",
"currency": "ISK",
"description": "Billing run 54321",
"reference": "410009982476",
"state": "settled",
"created_at": "2024-01-01T00:00:00+00:00",
"updated_at": "2024-01-01T00:00:00+00:00",
"fail_code": null,
"refund_code": null,
"cancel_code": null,
"payment_method": 1111,
"billing_run_id": 54321,
"billing_run_attempt_id": 65432,
"subscription_contract_id": 12345,
"customer": 33482,
"customer_reference": "customer-12345",
"legacy_subscription_ids": [
9876
]
}
Field |
Description |
|---|---|
|
ID of the transaction. |
|
Amount and currency of the transaction. |
|
Always |
|
The payment processor’s reference for the transaction. |
|
State of the transaction: |
|
When the transaction was created and last updated. |
|
The transaction’s codes if the charge failed, was refunded or was canceled. |
|
ID of the payment method as a number, not an object as in the legacy shape. |
|
The billing run that created the payment, and the attempt within it. |
|
The subscription contract being billed. |
|
ID of the customer as a number, and the customer reference. |
|
Legacy subscriptions that were migrated to the subscription contract. |
A payment.retry may carry no transaction, e.g. if the attempt failed before a transaction was created. The transaction fields (uuid, reference, created_at, updated_at, payment_method and the codes) are then null, amount and currency come from the billing run, and state is the billing run’s state, e.g. retry_scheduled, rather than a transaction state.
Checkout (checkout.*)
When a checkout is created or changed:
{
"token": "3a44383eb19dc9183be4df5ad655094c88159af4",
"checkout_url": "http://askell.is/checkout/3a44383eb19dc9183be4df5ad655094c88159af4/",
"status": "tokencreated"
}