Skip to main content

Migrating from Checkout V1 to V2

Checkout V2 (Delivery Checkout) is the evolution of V1 (SIW).

J
Written by Jakub Talarek

Checkout V2 (Delivery Checkout) is the evolution of V1 (SIW). This page covers what changes between them and the steps to move an existing integration across.

If you are still deciding which integration to use, start with Ingrid checkout API options: V1, V2, and Headless.

Why migrate to V2

V1 is legacy and no longer actively maintained. Your existing V1 integration continues to work and no retirement date has been announced — but V1 receives no new capabilities, so the gap between the two widens over time.

V2 unlocks what V1 cannot support:

  • Built-in address form — address collection, validation, autocomplete, and address book as part of the widget. No need to build or maintain your own address form

  • Delivery groups — support for split shipments and marketplace setups, with addresses and delivery options per group

  • Session pull — a dedicated endpoint that refreshes delivery promises without side effects on session state

  • Currency conversion — built-in support for cross-border currency handling

  • Ingrid AI features — predictive delivery times and profit-optimized delivery require V2

  • Return promise — return conditions shown in checkout and stored with the order, reused across tracking, returns, and post-purchase communication. Requires Ingrid Returns

  • Simplified session complete — when using the address form, there is no need to pass the customer object at completion

  • Richer event model — the summary_changed event provides address and billing data alongside pricing, reducing the need for additional API calls

Migrating also positions your integration for future Ingrid platform capabilities, which are built on the V2 architecture.

Key differences at a glance

Aspect

V1 (SIW)

V2 (Delivery Checkout)

Status

Legacy — no new capabilities

Current

API base path

/v1/siw/

/v1/delivery_checkout/

Address handling

No built-in form — you or your payment provider handle the address

Built-in address form with autocomplete, validation, address book

Session retrieval

session.get regenerates delivery promises

session.get is idempotent, with no side effects · session.pull refreshes delivery promises

Split shipments

Not supported — single cart only

Delivery groups — supports split shipments and marketplace

Currency conversion

Not supported

Built in

Customer data at create and update

customer accepted on session.create and session.update

customer only on session.complete

external_id

Optional

Required on session.complete

Cart item fields

quantity and discount optional

quantity and discount required

Response structure

selected_shipping_option and result

delivery_groups[] array, with a single entry if there is no split shipment

Address in response

search_address and customer at session root

Inside delivery_groups[].addresses

Frontend events

data_changed

data_changed and summary_changed, with address form and delivery groups

Ingrid AI support

Not available

Predictive delivery times and profit-optimized delivery

prefill_delivery_address

Not available

Optional on session.create — prefills the address form with known customer data

Migration checklist

Step 1: Update the API endpoint

Change your base path from the SIW API to the Delivery Checkout API.

V1

V2

Base path

/v1/siw/

/v1/delivery_checkout/

Create

POST /v1/siw/session.create

POST /v1/delivery_checkout/session.create

Get

GET /v1/siw/session.get

GET /v1/delivery_checkout/session.get

Pull

—

GET /v1/delivery_checkout/session.pull

Update

POST /v1/siw/session.update

POST /v1/delivery_checkout/session.update

Complete

POST /v1/siw/session.complete

POST /v1/delivery_checkout/session.complete

Authentication stays the same — a bearer token in the Authorization header.

Step 2: Move customer data to session complete

In V1, you could pass the customer field on session.create and session.update. In V2, customer is only accepted on session.complete.

What to change:

  • Remove customer from your session.create and session.update payloads

  • Make sure your session.complete call includes the full customer object — email, phone, address

Note: If you use the Ingrid address form, customer data is collected inside the widget and you do not need to pass the customer object at session.complete. The address is captured automatically.

Step 3: Pass external_id on session complete

In V2, external_id — your order reference — is required on session.complete.

What to change:

  • Make sure your session.complete payload includes external_id with your order or reference ID

{
  "checkout_session_id": "...",
  "external_id": "YOUR-ORDER-ID",
  "customer": { ... }
}

Step 4: Assess prefill_delivery_address

V2 introduces prefill_delivery_address on session.create. If you have customer address data available when checkout starts — from a logged-in profile or a cookie, for example — you can use it to pre-populate the address form.

What to decide:

  • Do you have address data available at session creation?

  • If yes, pass it in prefill_delivery_address on session.create to speed up checkout for returning customers

This is optional, and recommended for a better customer experience.

Step 5: Replace session.get with session.pull

In V1, session.get regenerated delivery promises — it was mutating. In V2, session.get is idempotent: it returns a snapshot without refreshing anything.

To preserve V1 behaviour, use session.pull instead.

V1

V2

Refresh delivery promises

session.get

session.pull

Read session without side effects

—

session.get

What to change:

  • Replace session.get calls with session.pull wherever you need fresh delivery promises — for example when a customer returns to an abandoned checkout

  • Use session.get only when you need a read-only snapshot of current state, such as backend verification after frontend events

Step 6: Add quantity and discount to cart items

In V2, quantity and discount are required on cart items.

What to change:

  • Make sure every item in cart.items[] includes quantity as an integer and discount as an integer in minor units — use 0 when there is no discount

{
  "cart": {
    "total_value": 50000,
    "items": [
      {
        "sku": "SHIRT-001",
        "name": "Classic T-Shirt",
        "quantity": 2,
        "price": 25000,
        "discount": 0
      }
    ],
    "cart_id": "cart-123"
  }
}

Step 7: Update response parsing from result to delivery_groups

The response structure has changed. What was result and selected_shipping_option in V1 is now delivery_groups[] in V2.

If you are not using split shipments, delivery_groups contains a single entry.

V1 response, simplified:

{
  "session": {
    "selected_shipping_option": { ... },
    "result": {
      "shipping_method": "postnord_home",
      "delivery_type": "delivery",
      "carrier": "PostNord",
      "price": { "currency": "SEK", "price": 4900 },
      "category": { "name": "Home Delivery" }
    },
    "search_address": { ... },
    "customer": { ... }
  }
}

V2 response, simplified:

{
  "session": {
    "delivery_groups": [
      {
        "shipping": {
          "carrier": "PostNord",
          "product": "postnord_home",
          "delivery_type": "delivery"
        },
        "pricing": {
          "price": 4900,
          "currency": "SEK"
        },
        "category": {
          "name": "Home Delivery"
        },
        "addresses": {
          "delivery_address": { ... },
          "billing_address": { ... },
          "customer": { ... },
          "location": { ... }
        },
        "tos_id": "..."
      }
    ]
  }
}

What to change:

  • Read from delivery_groups[0] instead of result

  • Read shipping data from delivery_groups[0].shipping

  • Read pricing from delivery_groups[0].pricing

  • Read tos_id from delivery_groups[0].tos_id

Step 8: Update address reading from the response

In V1, search_address and customer were at the session root. In V2 they have moved inside delivery_groups[].addresses.

What to change:

  • Read the delivery address from delivery_groups[0].addresses.delivery_address when using the address form

  • Read customer-provided data from delivery_groups[0].addresses.customer

  • Read the billing address from delivery_groups[0].addresses.billing_address, if enabled

  • Read pickup point details from delivery_groups[0].addresses.location

Note: If the shopper uses the address form, delivery_groups[0].addresses.customer contains the data they provided. This is the authoritative address — persist it to your order management system.

API mapping — quick reference

What you need

V1 (SIW)

V2 (Delivery Checkout)

Create session

POST /v1/siw/session.create

POST /v1/delivery_checkout/session.create

Get session, read-only

—

GET /v1/delivery_checkout/session.get

Refresh delivery promises

GET /v1/siw/session.get

GET /v1/delivery_checkout/session.pull

Update session

POST /v1/siw/session.update

POST /v1/delivery_checkout/session.update

Complete session

POST /v1/siw/session.complete

POST /v1/delivery_checkout/session.complete

Selected shipping method

session.result.shipping_method

session.delivery_groups[0].shipping.product

Carrier

session.result.carrier

session.delivery_groups[0].shipping.carrier

Delivery type

session.result.delivery_type

session.delivery_groups[0].shipping.delivery_type

Shipping price

session.result.price.price

session.delivery_groups[0].pricing.price

Category name

session.result.category.name

session.delivery_groups[0].category.name

Transport order ID

session.tos_id

session.delivery_groups[0].tos_id

Customer address

session.customer

session.delivery_groups[0].addresses.customer

Search address

session.search_address

session.delivery_groups[0].addresses.search_address

Delivery address, address form

—

session.delivery_groups[0].addresses.delivery_address

Pickup point

session.result.location

session.delivery_groups[0].addresses.location

FAQ

  • Do I need to migrate everything at once?
    Yes. V1 and V2 are separate APIs, so you switch your integration from one to the other. There is no hybrid mode.

  • Is V1 being deprecated?
    V1 is already treated as legacy and is no longer actively maintained. Existing integrations continue to work, and no formal retirement date has been announced. All new capability — Ingrid AI, the address form, delivery groups, the return promise — is V2 only, so the practical answer is to plan a migration rather than wait for a deadline.

  • Will my frontend break?
    Widget embedding works the same way: you still receive an html_snippet and embed it. The JavaScript API (window._sw) and the data_changed, suspend, and resume events remain available. V2 adds the summary_changed event for address form and delivery group use cases.

  • What if I don't use split shipments?
    ​delivery_groups contains a single entry. Read delivery_groups[0] wherever you previously read result.

  • Do I need to use the address form?
    No, it is optional. If you prefer to handle address collection yourself, or through a payment provider, you can disable the address form and pass search_address via session.update, as in V1. See How to structure an operationally viable and high-converting checkout with Ingrid Delivery Checkout for guidance on who should own the address form.

  • What changes on session complete if I use the address form?
    You no longer need to pass the customer object — the address is already captured by the widget. You still pass checkout_session_id and external_id. Read the authoritative address from delivery_groups[0].addresses.delivery_address in the response.

  • What about the transport order (TOS)?
    The session still completes as a transport order with all connected data. Ingrid Transport Administration works the same way whether V1 or V2 was used — it is not affected by this migration.

  • Where can I find the full API reference?
    ​Delivery Checkout API · SIW API, legacy

Related pages

Did this answer your question?