Cancellations third party guide

Please make sure you read the Conventions before continuing with this guide.

Table of Contents

Overview

This guide provides comprehensive documentation for performing cancellations using Betterez APIs by third parties. It covers the complete cancellation flow, channel-specific behaviors, how audit fields (createdBy and updatedBy) are set, error handling, and examples for all supported channels.

Prerequisites

You will need:

  • An X-API-KEY
  • A Basic Access Authentication token or JWT token
  • For backoffice channels: A back-office user account with appropriate permissions
  • For websales channels: A customer account or widget user configuration

Supported Channels

The cancellation API supports the following channels:

  1. backoffice - For cancellations performed by back-office users
  2. websales - For cancellations performed by customers through web sales
  3. agency-backoffice - For cancellations performed by agency back-office users
  4. agency-websales - For cancellations performed by agency customers through web sales

Complete Cancellation Flow

The cancellation process consists of three main steps:

  1. Get Cancellable Items - Retrieve items that can be cancelled from a transaction
  2. Create Cancel Set - Create a cancellation preview with calculated fees and refund amounts
  3. Execute Cancellation - Process the cancellation and refund payments

Step 1: Getting Cancellable Items

Endpoint

GET /operations/transactions/{transactionId}/cancellable-items

Description

This endpoint returns all items in a transaction that are eligible for cancellation, grouped by product family. Each item includes basic information needed to display cancellation options to users.

Request Parameters

Parameter Type Location Required Description
transactionId string path Yes The transaction ID containing items to cancel
displayAll boolean query No If true, returns all items regardless of cancellability status
channel string query Yes The channel performing the cancellation (backoffice, websales, agency-backoffice, agency-websales)

Channel-Specific Behavior

  • backoffice/agency-backoffice: Returns cancellable items based on back-office rules and cutoff times (unless allowReissueToAPastDate is enabled in account preferences - see Account Preferences in the back-office)
  • websales/agency-websales: Returns cancellable items based on customer-facing rules and cutoff times

Example Request

curl -X GET \
  "https://api.betterez.com/operations/transactions/507f191e810c19729de860ee/cancellable-items?channel=backoffice" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer your-jwt-token"

Response Structure

{
  "tickets": [
    {
      "_id": "507f191e810c19729de860ea",
      "accountId": "507f191e810c19729de86000",
      "availableUses": 1,
      "cancellable": true,
      "channel": "backoffice",
      "createdAt": {
        "value": "2022-10-03T16:02:52.716Z",
        "offset": 0
      },
      "displayTotal": 1130000,
      "departureTime": "01:10",
      "expirationDate": {
        "value": "2022-10-11T00:00:00.000Z",
        "offset": 0
      },
      "departureTimestamp": "2022-10-11T05:10:00.000Z",
      "isExpirationDateCalculated": false,
      "expire": 60,
      "expireUnit": "days",
      "fare": "Adult",
      "firstName": "S",
      "from": "Alban",
      "lastName": "N",
      "productFamily": "reservation",
      "productType": "reservation",
      "refId": "",
      "status": "paid",
      "ticketNumber": "A4NAG8",
      "to": "Barrie",
      "total": 1130000,
      "trxId": "507f191e810c19729de860ee",
      "expired": false
    }
  ],
  "flexPasses": [],
  "reservations": [],
  "parcels": [],
  "fees": [],
  "soldItems": [],
  "insurances": [],
  "redeemableItems": [],
  "giftCertificates": []
}

Items Not Showing as Cancellable

Items may not appear as cancellable if:

  • Item is already cancelled
  • Item is expired (past departure date or cutoff times)
  • Item is unpaid
  • No available uses left (for flex passes)
  • Item has been redeemed
  • Gift certificate has been used at least once
  • Parcel has been scanned

Note: If the displayAll query parameter is set to true, all items will be returned regardless of cancellability. However, attempting to cancel a non-cancellable item in the next step will result in an error.


Step 2: Creating a Cancel Set

Endpoint

POST /sales/cancellations

Description

This endpoint creates a cancellation preview (cancel set) that includes:

  • Detailed item information
  • Calculated cancellation fees
  • Refund amounts per payment method
  • Tax calculations
  • Payment method options for refunds

The cancel set is read-only and must be sent unmodified to the next step. If modified, the signature validation will fail.

Request Body

{
  "cancellation": {
    "trxId": "507f191e810c19729de860ee",
    "tickets": ["507f191e810c19729de860ea"],
    "reservations": [],
    "flexPasses": [],
    "parcels": [],
    "soldItems": [],
    "redeemableItems": [],
    "giftCertificates": [],
    "insurances": [],
    "fees": [],
    "channel": "backoffice",
    "penalty": {
      "amount": 0,
      "reason": ""
    },
    "overridedCancelFees": [],
    "currency": "USD",
    "terminalPayload": null
  }
}

Request Parameters for Cancel Set

Field Type Required Description
cancellation.trxId string Yes The transaction ID to cancel items from
cancellation.tickets array Yes Array of ticket IDs to cancel (can be empty)
cancellation.reservations array Yes Array of reservation IDs to cancel (can be empty)
cancellation.flexPasses array Yes Array of flex pass IDs to cancel (can be empty)
cancellation.parcels array Yes Array of parcel IDs to cancel (can be empty)
cancellation.soldItems array Yes Array of sold item IDs to cancel (can be empty)
cancellation.redeemableItems array Yes Array of redeemable item IDs to cancel (can be empty)
cancellation.giftCertificates array Yes Array of gift certificate IDs to cancel (can be empty)
cancellation.insurances array Yes Array of insurance IDs to cancel (can be empty)
cancellation.fees array Yes Array of transaction fee indices to cancel (can be empty)
cancellation.channel string Yes Channel identifier: backoffice, websales, agency-backoffice, or agency-websales
cancellation.penalty object Yes Penalty amount and reason (optional, defaults to 0)
cancellation.overridedCancelFees array No Array of cancel fees to override with reasons
cancellation.currency string No Currency code for refund (defaults to transaction currency)
cancellation.terminalPayload object No Terminal payment payload for in-person refunds

Channel-Specific Behavior

Backoffice Channel (backoffice)

  • User Authentication: Requires a back-office user JWT token
  • Cutoff Times: Respects cutoff times unless allowReissueToAPastDate is enabled in account preferences (configured in Account Preferences in the back-office)
  • Permissions: User must have appropriate cancellation permissions
  • Fee Override: Allowed if user has overrideCancelFees permission
  • Penalty: Allowed if account has enableArbitraryRefunds enabled and user has permission (configured in Account Preferences in the back-office)
  • Shift Requirement: May require an active shift depending on account configuration

Websales Channel (websales)

  • User Authentication: Uses customer JWT token or widget user
  • Cutoff Times: Always respects cutoff times
  • Fee Override: Not allowed - customer cannot override fees
  • Penalty: Not allowed - customer cannot add penalties
  • Shift Requirement: No shift required
  • User Resolution: The userId is extracted from the authenticated JWT token. If the JWT token does not contain user information, the system automatically uses the widget user configured for the account

Agency-Backoffice Channel (agency-backoffice)

  • User Authentication: Requires agency back-office user JWT token
  • Agency Validation: Account must be a valid agency for the transaction
  • Cutoff Times: Respects cutoff times unless allowReissueToAPastDate is enabled (configured in Account Preferences in the back-office)
  • Permissions: User must have appropriate cancellation permissions
  • Fee Override: Allowed if user has overrideCancelFees permission
  • Penalty: Allowed if account has enableArbitraryRefunds enabled (configured in Account Preferences in the back-office)

Agency-Websales Channel (agency-websales)

  • User Authentication: Uses agency customer JWT token or widget user
  • Agency Validation: Account must be a valid agency for the transaction
  • Cutoff Times: Always respects cutoff times
  • Fee Override: Not allowed
  • Penalty: Not allowed
  • Shift Requirement: No shift required

Optional: Adding a Penalty

To add a penalty amount to the cancellation:

{
  "cancellation": {
    "trxId": "507f191e810c19729de860ee",
    "tickets": ["507f191e810c19729de860ea"],
    "channel": "backoffice",
    "penalty": {
      "amount": 100000,
      "reason": "Late cancellation request"
    }
  }
}

Requirements:

  • Channel must be backoffice or agency-backoffice
  • Account must have enableArbitraryRefunds enabled (configured in Account Preferences in the back-office)
  • User must have permission to apply penalties
  • Penalty amount cannot exceed refundable amount

Optional: Overriding Cancel Fees

To prevent specific cancel fees from applying:

{
  "cancellation": {
    "trxId": "507f191e810c19729de860ee",
    "tickets": ["507f191e810c19729de860ea"],
    "channel": "backoffice",
    "overridedCancelFees": [
      {
        "internalId": "cancel-fee-001",
        "reason": "Customer service exception"
      }
    ]
  }
}

Requirements:

  • Channel must be backoffice or agency-backoffice
  • User must have overrideCancelFees permission
  • Each override must include a reason

Response Structure

{
  "_id": "507f191e810c19729de860ea",
  "cancellation": {
    "trxId": "507f191e810c19729de860ee",
    "transactionId": "ABCDFG12",
    "customerNumber": "123-456-789",
    "tickets": [
      {
        "_id": "507f191e810c19729de860ea",
        "trxId": "507f191e810c19729de860ee",
        "accountId": "507f191e810c19729de86000",
        "ticketNumber": "ABCDEF",
        "ticketId": "507f191e810c19729de860ea",
        "productFamily": "ticket",
        "productType": "ticket",
        "productId": "507f191e810c19729de860ea",
        "totalRefundable": 6150000,
        "displayTotalRefundable": 6150000,
        "rounding": {
          "policy": "default",
          "decimals": "2"
        },
        "fareClassId": null,
        "fees": [],
        "taxes": [],
        "status": "paid",
        "ratios": [
          {
            "type": "cash",
            "total": 6260000,
            "displayTotal": 6260000,
            "extraInfo": null
          }
        ],
        "cancelFees": [
          {
            "calculated": 615000,
            "_id": "507f191e810c19729de860ea",
            "userId": "507f191e810c19729de860ea",
            "accountId": "507f191e810c19729de860ea",
            "internalId": "Example cancel fee",
            "name": "Example cancel fee",
            "valueType": "%",
            "value": 10000,
            "taxable": true,
            "isRefundable": true,
            "valueToDisplay": "10",
            "displayCalculated": 615000,
            "taxes": [
              {
                "name": "ontax",
                "value": 5000,
                "province": "Ontario",
                "calculated": 28000,
                "displayCalculated": 28000
              }
            ],
            "taxesTotal": 56000,
            "displayTaxesTotal": 56000
          }
        ],
        "expirationDate": {},
        "expiryDate": null,
        "price": 4590000,
        "total": 6260000,
        "displayPrice": 4590000,
        "displayTotal": 6260000,
        "subTotal": 0,
        "totalTaxes": 560000,
        "displaySubTotal": 0,
        "displayTotalTaxes": 560000,
        "flexPassOptions": {},
        "currentScansPerDay": {},
        "availableUses": 1,
        "totalCancelFees": 615000,
        "displayTotalCancelFees": 615000,
        "createdFromFlexpassId": null,
        "payment": [
          {
            "provider": "inperson",
            "type": "cash",
            "ticketNumber": "ABCDEF",
            "amount": 5535000,
            "acceptedCurrencyAmount": 5535000,
            "percentage": 100
          }
        ],
        "refunded": 5535000,
        "displayRefunded": 5535000,
        "itemType": "reservation",
        "totalRefundablePreTax": 5590000,
        "displayTotalRefundablePreTax": 5590000
      }
    ],
    "reservations": [],
    "soldItems": [],
    "flexPasses": [],
    "redeemableItems": [],
    "giftCertificates": [],
    "parcels": [],
    "insurances": [],
    "trxFees": [],
    "cancelFees": [
      {
        "internalId": "Example cancel fee",
        "name": "Example cancel fee",
        "calculated": 616000,
        "displayCalculated": 616000,
        "taxesTotal": 56000,
        "value": 10000,
        "valueType": "%"
      }
    ],
    "overridedCancelFees": [],
    "displayCurrency": {
      "isocode": "USD",
      "symbol": "$",
      "buy": 1,
      "sell": 1,
      "channels": ["backoffice"]
    },
    "penalty": {
      "amount": 0,
      "acceptedAmount": 0,
      "reason": ""
    },
    "payments": [
      {
        "_id": "a1b2c3d4-abdc-abcd-1234-a1b2c3d4f555",
        "method": "cash",
        "provider": "inperson",
        "displayName": "cash",
        "firstName": "FirstName",
        "lastName": "LastName",
        "currency": "USD",
        "card": "",
        "authorization": "",
        "amount": "55.44",
        "acceptedCurrencyAmount": "55.44",
        "feesAndPenaltyAmount": "6.16",
        "displayFeesAndPenaltyAmount": "6.16",
        "feesAndPenaltyAmountValue": 616000,
        "displayFeesAndPenaltyAmountValue": 616000,
        "amountValue": 5544000,
        "acceptedCurrencyAmountValue": 5544000
      }
    ],
    "channel": "backoffice"
  },
  "signature": "86734655de9f997daddb61927f1a4111918d777493d748300689c0b0a55a87c899cad21b0b08c80a3c534b687cedf5ff",
  "dupKey": "5cfd287b1ebb1983fd47ca61cc1e1e96cab3cd0e519e8a8b9021692a58b817a1e5c78dd892d41ad59b92637bc4528f8f"
}

Important:

  • The signature field is used to verify the cancel set has not been modified. Do not modify any fields in the response before sending it to the next step.
  • The dupKey field is used for idempotency to prevent duplicate cancellations. It is automatically generated by the system and must be included in the PUT request. If the same dupKey is submitted within 30 seconds, the request will be rejected with a 409 Conflict error indicating the cancellation is already in progress.

Step 3: Executing the Cancellation

Endpoint

PUT /sales/cancellations

Description

This endpoint processes the cancellation and executes refunds through payment providers. It:

  • Validates the cancel set signature
  • Updates item statuses to "cancelled" or "refunding"
  • Processes refund payments through payment providers
  • Creates a new refund transaction
  • Updates the original transaction
  • Emits webhook events

Request Body

Send the complete, unmodified cancel set response from Step 2:

{
  "cancelSet": {
    "_id": "507f191e810c19729de860ea",
    "cancellation": {
      // ... complete cancel set from Step 2
    },
    "signature": "86734655de9f997daddb61927f1a4111918d777493d748300689c0b0a55a87c899cad21b0b08c80a3c534b687cedf5ff",
    "dupKey": "5cfd287b1ebb1983fd47ca61cc1e1e96cab3cd0e519e8a8b9021692a58b817a1e5c78dd892d41ad59b92637bc4528f8f"
  }
}

Note: The dupKey field is automatically included in the cancel set response from Step 2. It must be included in the PUT request to enable idempotency protection against duplicate submissions.

Channel-Specific Behavior for Execution

Backoffice Channel Execution (backoffice)

  • User Resolution: Uses the user information from the authenticated JWT token. The userId is automatically extracted from the JWT token when a back-office user authenticates
  • Shift Requirement: May require an active shift if user has shift permissions
  • Payment Processing: Supports all payment methods available for the account
  • Terminal Payload: Can include terminal payload for in-person refunds

Websales Channel Execution (websales)

  • User Resolution:
    • If the JWT token contains user information, uses that user's ID as the userId
    • Otherwise, if no user information is present in the JWT token, uses the widget user configured for the account
  • Customer Validation: Validates that the customer number in the JWT token matches the transaction customer
  • Shift Requirement: No shift required
  • Payment Processing: Limited to online payment methods typically
  • Terminal Payload: Not applicable

Agency-Backoffice Channel Execution (agency-backoffice)

  • User Resolution: Uses the agency user information from the authenticated JWT token. The userId is automatically extracted from the JWT token when an agency back-office user authenticates
  • Agency Validation: Validates account is a valid agency for the transaction
  • Shift Requirement: May require an active shift
  • Payment Processing: Supports payment methods available to the agency
  • Interline Handling: May create consumer transaction for interline accounting

Agency-Websales Channel Execution (agency-websales)

  • User Resolution:
    • If the JWT token contains user information, uses that user
    • Otherwise, uses the widget user configured for the agency account
  • Customer Validation: Validates customer matches transaction
  • Agency Validation: Validates account is a valid agency
  • Shift Requirement: No shift required
  • Payment Processing: Limited to online payment methods

Response Structure

{
  "transaction": {
    "_id": "507f191e810c19729de860eb",
    "transactionId": "REFUND01",
    "status": "cancelled",
    "accountId": "507f191e810c19729de86000",
    "associatedTransactionId": "ABCDFG12",
    "associatedTrxId": "507f191e810c19729de860ee",
    "customerNumber": "123-456-789",
    "total": -5535000,
    "displayTotal": -55.35,
    "summary": [
      {
        "_id": "507f191e810c19729de860ea",
        "status": "cancelled",
        "refunded": true,
        "refId": "507f191e810c19729de860ec",
        "refundId": "R-ABCDEF",
        "total": -5535000,
        "displayTotal": -55.35
      }
    ],
    "payments": [
      {
        "method": "cash",
        "provider": "inperson",
        "amount": -55.44,
        "status": "success"
      }
    ],
    "createdBy": "507f191e810c19729de860ed",
    "createdByUserEmail": "user@example.com",
    "createdAt": {
      "value": "2022-10-03T16:05:00.000Z",
      "offset": 0
    },
    "originalPurchaseChannel": "backoffice"
  }
}

Important Notes

Single Online Payment Limitation: At the moment, it is only possible to refund a single online payment at a time. If there are multiple online payment methods in the same transaction and no override payment was provided, the cancellation will not be performed and will return a MORE_THAN_ONE_ONLINE_CREDIT_CARD error. To handle transactions with multiple online payments, you must provide an override payment method in the cancellation request.


Audit Fields: createdBy and updatedBy

The system sets audit fields (createdBy, updatedBy, createdByUserEmail, updatedByUserEmail) differently based on the channel and context.

Transaction Object

createdBy / createdByUserEmail

Set when the refund transaction is created:

  • Backoffice/Agency-Backoffice:

    • createdBy: Set to the user ID from the JWT token
    • createdByUserEmail: Set to the user email from the JWT token
    • If the JWT token does not contain user information, these fields are null
  • Websales/Agency-Websales:

    • createdBy: Set to the widget user ID if the JWT token does not contain user information, otherwise set to the user ID from the JWT token
    • createdByUserEmail: Set to the widget user email if the JWT token does not contain user information, otherwise set to the user email from the JWT token
    • The widget user is automatically selected from the account configuration

updatedBy / updatedByUserEmail

  • Set when the original transaction is updated during cancellation
  • Always set to the user performing the cancellation (same logic as createdBy)

Refund Object

createdBy / createdByUserEmail

  • Set to the same user as the transaction createdBy
  • Determined from the user information in the JWT token used for the cancellation request

updatedBy / updatedByUserEmail

  • Initially set to createdBy values
  • Updated when refund status changes

Cancelled Items (Tickets, Parcels, etc.)

updatedBy / updatedByUserEmail

  • Set when item status is updated to "cancelled" or "refunding"
  • Always set to the user email from the JWT token used in the cancellation request

Original Transaction

updatedBy / updatedByUserEmail

  • Set when transaction summary items are updated with refund information
  • Always set to the user email from the JWT token used in the cancellation request

Summary Table

Object Field Backoffice Websales Agency-Backoffice Agency-Websales
Refund Transaction createdBy User ID from JWT token Widget user ID or user ID from JWT token User ID from JWT token Widget user ID or user ID from JWT token
Refund Transaction createdByUserEmail User email from JWT token Widget user email or user email from JWT token User email from JWT token Widget user email or user email from JWT token
Refund Object createdBy User ID from JWT token Widget user ID or user ID from JWT token User ID from JWT token Widget user ID or user ID from JWT token
Refund Object createdByUserEmail User email from JWT token Widget user email or user email from JWT token User email from JWT token Widget user email or user email from JWT token
Cancelled Items updatedByUserEmail User email from JWT token Widget user email or user email from JWT token User email from JWT token Widget user email or user email from JWT token
Original Transaction updatedByUserEmail User email from JWT token Widget user email or user email from JWT token User email from JWT token Widget user email or user email from JWT token

Error Handling

Common Errors

400 Bad Request

Error Code Description Solution
WRONG_DATA Missing required cancellation data Ensure all required fields are provided
INVALID_ITEM_ID Invalid item ID provided Verify item IDs match those from cancellable items endpoint
INVALID_DOCUMENT Invalid document structure Check request body structure matches API specification
MISSING_CANCELLABLE_IDS Missing or invalid refundable model IDs Ensure all item IDs are valid and belong to the transaction
NO_REFUNDABLE_METHODS No refundable payment methods available Check payment methods configuration
MORE_THAN_ONE_ONLINE_CREDIT_CARD Multiple online credit card payments found Only one online credit card refund allowed at a time
REMAINING_FEE_OR_PENALTY Outstanding balance after fees/penalty Adjust penalty or fee override amounts
NO_REFUNDABLES_FOUND No items found for given IDs Verify item IDs are correct
TRX_FEE_MISSING_REFUNDABLES Transaction fee refund requires all items Select all items when refunding transaction fees
INVALID_FEE_OVERRIDE Fee override sent but no cancel fee found Verify cancel fee internalId exists
INVALID_FEE_OVERRIDE_REASON Fee override missing reason Provide reason for each fee override
NO_PENALTY_REASON Penalty amount set without reason Provide reason when setting penalty
PENALTY_NOT_ALLOWED Arbitrary refunds disabled Enable enableArbitraryRefunds in account preferences (configured in Account Preferences in the back-office)
DIFFERENT_FARE_CLASSES Tickets have different fare classes Only cancel tickets with same fare class together
INVALID_PENALTY Penalty exceeds refundable amount or is negative Adjust penalty amount
INVALID_CANCEL_SET Missing fields in cancel set Ensure cancel set is complete
INVALID_SIGNATURE Cancel set signature mismatch Do not modify cancel set between POST and PUT
[itemType]_NOT_FOUND Item not found Verify item ID and transaction ID
[itemType]_NOT_REFUNDABLE Item is not refundable Check item status and cutoff times
SHIFT_REQUIRED_TO_REFUND Active shift required Open a shift for the user
SHIFT_STATION_NOT_FOUND Shift station not found Verify user shift configuration
REQUIRED_COMPANION_ERROR Companion tickets must be cancelled together Include all companion tickets in cancellation
INVALID_TERMINAL_PAYLOAD Invalid terminal payload Verify terminal payload structure
UNUSED_TERMINAL_PAYLOAD Terminal payload provided but not used Remove unused terminal payload
MISSING_TERMINAL_PAYLOAD Terminal payload required but missing Provide terminal payload for in-person refunds
MISSING_REFERENCED_TRX Missing referenced transaction Verify transaction association
INVALID_AUTH_CODE Missing authorization code Provide authorization code for refund payment
CURRENCY_EXCHANGE_MISMATCH Currency mismatch with station Use correct currency for refund
AGENCY_CURRENCY_MISMATCH Currency mismatch with provider Use correct currency for agency refunds

401 Unauthorized

Error Code Description Solution
UNAUTHORIZED Missing or invalid X-API-KEY or Authorization header Verify API key and token are correct

403 Forbidden

Error Code Description Solution
NOT_ALLOWED_FOR_ONLINE Insufficient permissions for online refunds User needs online refund permissions
FEE_OVERRIDE_NOT_ALLOWED User cannot override cancel fees User needs fee override permissions
INVALID_AGENCY Account is not a valid agency Verify agency relationship

404 Not Found

Error Code Description Solution
TRANSACTION_NOT_FOUND Transaction not found Verify transaction ID
CUSTOMER_NOT_FOUND Customer not found Verify customer number

409 Conflict

Error Code Description Solution
CONFLICT Cancellation already in progress This error occurs when the same dupKey is submitted within 30 seconds, indicating a duplicate cancellation attempt. Wait for the current cancellation to complete or ensure you're not resubmitting the same cancel set

500 Internal Server Error

Error Code Description Solution
MISSING_UPDATE_MODEL Unimplemented update model Contact support
PAYMENT_REFUND_NOT_IMPLEMENTED Payment method refund not implemented Contact support
PAYMENT_ROLLBACK_FAILED Refund rollback failed Contact support
REFUND_EXECUTION_ERROR Payment provider error during refund Check payment provider status
[PROVIDER]_ERROR Specific payment provider error Check provider-specific error details

Error Response Format

{
  "code": "INVALID_ITEM_ID",
  "message": "Invalid item ID",
  "detail": "Additional error details if available"
}

Complete Examples

Example 1: Backoffice Cancellation

# Step 1: Get cancellable items
curl -X GET \
  "https://api.betterez.com/operations/transactions/507f191e810c19729de860ee/cancellable-items?channel=backoffice" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer backoffice-user-jwt-token"

# Step 2: Create cancel set
curl -X POST \
  "https://api.betterez.com/sales/cancellations" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer backoffice-user-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation": {
      "trxId": "507f191e810c19729de860ee",
      "tickets": ["507f191e810c19729de860ea"],
      "reservations": [],
      "flexPasses": [],
      "parcels": [],
      "soldItems": [],
      "redeemableItems": [],
      "giftCertificates": [],
      "insurances": [],
      "fees": [],
      "channel": "backoffice",
      "penalty": {
        "amount": 0,
        "reason": ""
      }
    }
  }'

# Step 3: Execute cancellation (use complete response from Step 2)
curl -X PUT \
  "https://api.betterez.com/sales/cancellations" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer backoffice-user-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "cancelSet": {
      // ... complete cancel set from Step 2 response
    }
  }'

Example 2: Websales Cancellation

# Step 1: Get cancellable items
curl -X GET \
  "https://api.betterez.com/operations/transactions/507f191e810c19729de860ee/cancellable-items?channel=websales" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer customer-jwt-token"

# Step 2: Create cancel set
curl -X POST \
  "https://api.betterez.com/sales/cancellations" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer customer-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation": {
      "trxId": "507f191e810c19729de860ee",
      "tickets": ["507f191e810c19729de860ea"],
      "reservations": [],
      "flexPasses": [],
      "parcels": [],
      "soldItems": [],
      "redeemableItems": [],
      "giftCertificates": [],
      "insurances": [],
      "fees": [],
      "channel": "websales",
      "penalty": {
        "amount": 0,
        "reason": ""
      }
    }
  }'

# Step 3: Execute cancellation
curl -X PUT \
  "https://api.betterez.com/sales/cancellations" \
  -H "X-API-KEY: your-api-key" \
  -H "Authorization: Bearer customer-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "cancelSet": {
      // ... complete cancel set from Step 2 response
    }
  }'

Example 3: Agency-Backoffice Cancellation with Fee Override

# Step 2: Create cancel set with fee override
curl -X POST \
  "https://api.betterez.com/sales/cancellations" \
  -H "X-API-KEY: agency-api-key" \
  -H "Authorization: Bearer agency-user-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation": {
      "trxId": "507f191e810c19729de860ee",
      "tickets": ["507f191e810c19729de860ea"],
      "reservations": [],
      "flexPasses": [],
      "parcels": [],
      "soldItems": [],
      "redeemableItems": [],
      "giftCertificates": [],
      "insurances": [],
      "fees": [],
      "channel": "agency-backoffice",
      "penalty": {
        "amount": 0,
        "reason": ""
      },
      "overridedCancelFees": [
        {
          "internalId": "cancel-fee-001",
          "reason": "Customer service exception - weather delay"
        }
      ]
    }
  }'

Best Practices

  1. Always use the channel parameter: Specify the correct channel in all requests to ensure proper validation and behavior

  2. Handle errors gracefully: Implement retry logic for transient errors (500, 409) but not for validation errors (400)

  3. Validate cancel set signature: Never modify the cancel set between POST and PUT requests

  4. Check item cancellability: Use the cancellable items endpoint before attempting cancellation

  5. Monitor webhook events: Subscribe to transaction and refund webhooks to track cancellation status

  6. Currency handling: Ensure refund currency matches the original transaction currency or is supported by the account

  7. Idempotency: The dupKey field is automatically included in the cancel set response and must be sent in the PUT request. This prevents duplicate cancellations - if the same dupKey is submitted within 30 seconds, the request will be rejected with a 409 Conflict error


Additional Resources

API Documentation

For detailed API specifications, refer to the interactive API documentation:

Postman Resources

If you would like to use Postman to test the cancellation API, we have included a collection you can use as a starting point.

This collection is designed to allow you to perform a cancellation by following the steps above. To use it successfully, you will need:

  • Your X-API-KEY
  • JWT Token
  • A cancellable transaction ID
  • Account data (like stations, purchases, payment types, etc.) set up for your account

After importing the collection and the environment, make sure your collection is using the new environment, and that you have set the initial values.

On each step, each pre-script and test script will populate environment variables needed for the next step. You will only need to set the transactionId manually.

If you are going to be using our API in sandbox, please change the {{basePath}} environment variable to https://sandbox-api.betterez.com.


Support

For additional support or questions about cancellations:

  • Review the API documentation in Swagger/ReDoc
  • Contact Betterez support at support@betterez.com
  • Check account-specific configuration in the back-office