Skip to Content
Get StartedMigrating from V0

Migrating from V0

Overview

Note

The Beam V0 API shuts down on 1 July 2027. Migrate every integration to the V1 API before that date.

The V0 API (partner-api.beamdata.co) is deprecated and is replaced by the V1 API (api.beamcheckout.com). Migration is easier than it looks: every V0 endpoint already stores data in V1 format to Payment Links, Charges, and Refunds.

Reasons to Migrate to V1

  • Clearer model. V0 packed the whole payment lifecycle into one Purchase object; V1 uses separate Payment Link, Charge, and Refund resources that each do one job, so requests and responses are easy to follow (see Concept).
  • Your integration stays stable. Beam can add new payment methods and features without changing the fields you already use.
  • Future-proof. New Beam features ship to V1 only — V0 receives none.

Concept

V0 modelled everything as one Purchase: the payment request and its result lived in the same record, which you also queried for customer data, status, and refunds.

V1 splits that lifecycle into three resources, each holding one part:

  • Payment Link — the request, and the direct successor to the Purchase. It is a Beam-hosted checkout page: create one for an order, redirect the shopper to its url, and Beam collects payment. It does not hold the result. The result (who paid, how) lives in Charge, not the Payment Link.
  • Charge — one payment attempt. A payment link can have several; only a SUCCEEDED charge means the shopper paid, and customer and payment-method data come from it.
  • Refund — tied to a chargeId, not a payment link. Save the chargeId from the charge.succeeded webhook so you can refund later.
V0 conceptV1 API concept
PurchasePayment Link
Purchase IDPayment Link ID
Payment IDCharge ID for completed payments
Purchase statePayment Link status and Charge status
Refund by purchase IDRefund by charge ID, then lookup by Refund ID

Before you start

V1 uses HTTP Basic Auth over HTTPS, and the Merchant ID is no longer in the URL path — Beam reads it from your credentials. Authenticate with your Merchant API Key; partners use their Partner API Key plus the X-Beam-Partner-ID header. See Authentication.

You don’t have to switch everything at once. V0 and V1 share the same underlying data, so you can run both in parallel, move traffic to V1 gradually, and retire V0 before the shutdown date.

Flow differences

In V0 you created one Purchase and read everything back from it. In V1 the same job is split across three resources (see Concept), so your API calls change. The table maps each step of the old flow to the new one.

V0 paths start from https://partner-api.beamdata.co; V1 paths start from https://api.beamcheckout.com.

StepV0 (partner-api.beamdata.co)V1 (api.beamcheckout.com)
1. CreatePOST /purchases/{merchantId}purchaseId + payment URLPOST /api/v1/payment-linksid + url
2. RedirectSend the shopper to the URLSend the shopper to url
3. Get notifiedpurchase.succeeded webhook (optional X-Hub-Signature)payment_link.paid + charge.succeeded webhooks (HMAC-SHA256) — save the chargeId
4. Read resultGET /purchases/{merchantId}/{purchaseId}/detailGET /api/v1/charges?source_in=PAYMENT_LINK&sourceId={paymentLinkId} — use the SUCCEEDED charge
5. RefundPOST /refunds/{merchantId}/{purchaseId} (by purchaseId)POST /api/v1/refunds (by chargeId)

Other operations:

OperationV0V1Reference
Get a payment linkGET /purchases/{merchantId}/{purchaseId}GET /api/v1/payment-links/{paymentLinkId}Payment Links API
Disable a payment linkPOST /purchases/{merchantId}/{purchaseId}/disablePATCH /api/v1/payment-links/{paymentLinkId}/disablePayment Links API
Find a link’s chargesGET /purchases/{merchantId}/{purchaseId}/detailGET /api/v1/charges?source_in=PAYMENT_LINK&sourceId={paymentLinkId}Charges API
Create a refundPOST /refunds/{merchantId}/{purchaseId}POST /api/v1/refunds (by chargeId)Refunds API
Look up a refundGET /refunds/{merchantId}/{purchaseId}GET /api/v1/refunds/{refundId}Refunds API

Migration checklist

  1. Switch the base URL to api.beamcheckout.com (update your DNS allowlists and firewalls).
  2. Update server-side auth to V1 credentials — HTTP Basic Auth, with no {merchantId} in the path.
  3. Replace purchase creation with POST /api/v1/payment-links, and save the returned id as your paymentLinkId.
  4. Redirect shoppers to the returned url.
  5. Set up a webhook for payment_link.paid and charge.succeeded, and save each chargeId.
  6. Replace /detail reads with GET /api/v1/charges?source_in=PAYMENT_LINK&sourceId={paymentLinkId}.
  7. Replace refunds with POST /api/v1/refunds (by chargeId), and save the returned refundId.
  8. Add x-beam-idempotency-key to all POST and PATCH retries. See Idempotency.
  9. Test the full flow on Playground before switching production traffic, and confirm your records reconcile with V1 IDs and amount units.

What else to watch for

  • Status codes changed. Create returns 201 Created; disable returns 202 Accepted. V0 returned 200 for both. Update any code that checks for exactly 200.
  • Amounts are in the smallest currency unit. 900 becomes 90000 for THB. See Field mapping.
  • Refunds use a chargeId, not a payment link ID. Partial refunds are CARD-only, and the V0 option and type fields are gone. See Refunds API.
  • Webhooks are signed and split. V1 signs every webhook with HMAC-SHA256 over the raw body, and replaces the single purchase.succeeded event with payment_link.paid and charge.succeeded. See Webhooks.
  • /detail is gone. Read customer and payment-method data from the SUCCEEDED charge instead, and note that less customer data is returned than in V0. See Detail response replacement.
  • Errors are structured. Branch on error.errorCode, not the message. See Error responses.
  • Playground and Production keys differ. A 401 INVALID_CREDENTIALS_ERROR usually means a Playground key was used against Production (or the wrong key type). See Authentication.
  • Some request fields changed. order.currency is required, order.totalAmount and order.totalDiscount are removed (send the final amount as netAmount), and collectPhoneNumber defaults to true. See Field mapping.
  • installmentOptions is restructured. See Installment options.

Webhooks

Migrate legacy events to V1 webhooks

V0 sent one event, purchase.succeeded. In V1 this becomes two events: payment_link.paid (fulfill the order) and charge.succeeded (save the chargeId for future refunds). Register your webhook URL in Lighthouse  under Developers and subscribe to both. See Event Types for each event’s payload.

V1 signs every webhook with HMAC-SHA256 over the raw, unformatted request body — reformatting the JSON before hashing breaks the signature. See Webhook authentication.

Migrate V0 state polling

Replace V0 state polling with webhooks. If you must poll, use the charges query instead. See List Charges API.

V0 stateWhat it meansV1 replacement
initiated / beamed / payingNot paid yetPaymentLink status == ACTIVE and no SUCCEEDED charge
completePayment succeededListen to payment_link.paid or charge.succeeded, or query charges for a SUCCEEDED charge
failedPayment attempt failedListen to charge.failed, or query charges and look for a FAILED charge

The request body is reshaped: amounts use the smallest currency unit, order fields are renamed, orderItems flatten out of the nested product object, and payment methods move into linkSettings. The example below omits orderItems for brevity — see Field mapping for the full list, or Payment Links API for the complete schema.

Before (V0):

{ "order": { "currencyCode": "THB", "description": "1 Cookies Box", "merchantReference": "cookies order 1", "merchantReferenceId": "merchantRef1", "netAmount": 900, "totalAmount": 1000, "totalDiscount": 100, "orderItems": [ { "product": { "name": "Cookies", "price": 1000, "sku": "string" }, "quantity": 1 } ] }, "redirectUrl": "string", "requiredFieldsFormId": "formId123", "supportedPaymentMethods": [ "creditCard" ] }

After (V1 API):

{ "order": { "currency": "THB", "description": "1 Cookies Box", "internalNote": "cookies order 1", "referenceId": "merchantRef1", "netAmount": 90000 }, "expiresAt": "2030-01-01T00:00:00.000Z", "redirectUrl": "https://example.com/payment-complete", "collectPhoneNumber": false, "linkSettings": { "card": { "isEnabled": true }, "qrPromptPay": { "isEnabled": true } } }

End-to-end example

How the pieces connect in a single payment:

  1. CreatePOST /api/v1/payment-links returns id (use it as your paymentLinkId) and url. See Payment Links API.
  2. Redirect the shopper to url; Beam hosts the checkout.
  3. Receive webhookspayment_link.paid and charge.succeeded. The charge.succeeded body is the Charge; save its chargeId. See Event Types.
  4. Read details (optional)GET /api/v1/charges?source_in=PAYMENT_LINK&sourceId={paymentLinkId}; use the SUCCEEDED charge for customer and payment-method data. See Charges API.
  5. Refund (later)POST /api/v1/refunds with the saved chargeId; store the returned refundId. See Refunds API.

The IDs chain through the flow: paymentLinkIdchargeIdrefundId. Keep each one as you go — you cannot refund without the chargeId.

Error responses

The V1 API returns a consistent error format on every 4xx/5xx response. Check error.errorCode (not message) to decide what to do. See Error Handling for the full enum.

Appendix

Field-level reference for the migration: converting V0 request payloads and replacing the V0 detail response. Use these tables once your main flow works.

Installment options

Before: "installmentOptions": [{ "period": 3 }, { "period": 6 }]

After:

{ "linkSettings": { "cardInstallments": { "isEnabled": true, "installments3m": { "isEnabled": true }, "installments4m": { "isEnabled": false }, "installments6m": { "isEnabled": true }, "installments10m": { "isEnabled": false } } } }

Detail response replacement

  • Payment link details → GET /api/v1/payment-links/{paymentLinkId}
  • Customer and payment-method data → GET /api/v1/charges?source_in=PAYMENT_LINK&sourceId={paymentLinkId}
V0 detail fieldV1 API source
purchaseIdPayment Link paymentLinkId
genericPurchaseIdNo public equivalent. Track the link yourself if you used this.
merchantIdPayment Link, Charge, and Refund merchantId
paymentIdNo equivalent. Use paymentLinkId (link) and chargeId (paid operations).
paymentLinkPayment Link url
orderPayment Link order
redirectUrlPayment Link redirectUrl
expiryPayment Link expiresAt
isDisabledPayment Link status == "DISABLED"
statePayment Link status and Charge status
merchantBasicInfo.name, merchantBasicInfo.logoUrlNot in the public API — keep merchant display info in your own system.
merchantBasicInfo.availablePaymentMethodsUse Payment Link linkSettings plus the methods enabled for your account.
requiredFieldsFormIdPayment Link collectDeliveryAddress and collectPhoneNumber
customerSUCCEEDED Charge customer (only primaryPhone, email, deliveryAddress). V0 firstName, lastName, title, dateOfBirth, billingAddress, social handles, and additionalField0..9 have no equivalent.
paymentMethodSUCCEEDED Charge paymentMethod.paymentMethodType (e.g. CARD, QR_PROMPT_PAY).
installmentPeriodSUCCEEDED Charge paymentMethod.cardInstallments.installmentPeriod (months). Only when paymentMethodType == CARD_INSTALLMENTS.
createdSUCCEEDED Charge transactionTime (payment time), or Charge createdAt. Not on the public Payment Links API.
lastUpdatedCharge updatedAt. Not on the public Payment Links API.
originator.emailNot in the public API — track the creator yourself.
refundsStore refundId from POST /api/v1/refunds, then call GET /api/v1/refunds/{refundId}.

Payment method mapping

V0 payment methodV1 API payment link setting
creditCardlinkSettings.card.isEnabled
installmentsCclinkSettings.cardInstallments.isEnabled
qrThblinkSettings.qrPromptPay.isEnabled
eWalletlinkSettings.eWallets.isEnabled
internetBankinglinkSettings.mobileBanking.isEnabled
bnpllinkSettings.buyNowPayLater.isEnabled

Field mapping

V0 fieldV1 API fieldNotes
order.netAmountorder.netAmountConvert to the smallest currency unit. 900 becomes 90000 for THB.
order.currencyCodeorder.currencyRenamed. V1 requires currency; currently only THB is supported.
order.merchantReferenceIdorder.referenceIdUse this for your order ID or matching reference.
order.merchantReferenceorder.internalNoteInternal merchant note.
order.totalAmountNo direct equivalentThe V1 API charges order.netAmount. Calculate discounts and the total amount in your own order system.
order.totalDiscountNo direct equivalentSend the final payable amount as order.netAmount.
order.orderItems[].product.*order.orderItems[].*Fields move out of the nested product object to the top level (e.g. nameitemName); quantity is unchanged. Convert price to the smallest currency unit.
(no V0 equivalent)order.orderItems[].productIdOptional internal product identifier.
expiryexpiresAtRenamed.
requiredFieldsFormIdcollectDeliveryAddress and collectPhoneNumberUse collectDeliveryAddress: true when you need delivery address collection. Phone number collection is enabled by default.
supportedPaymentMethodslinkSettingsConfigure payment methods explicitly in the payment link request.
Failure reason on a failed paymentCharge failureCodeRead failureCode from the Charge object (also present in webhook payloads); it is one of the CH_* enum codes and is absent when the charge did not fail.
Last updated on