ECR Payments Guide

This guide explains how to use the API for the Pos payment method for:

Authentication

The API supports both OAuth 2.0 and API key authentication. The API key flow is recommended for most integrations and used in this guide. Follow the steps on the authentication pages for a more in depth look into the topic.

API key

Send your API key in the header: Buck-Api-Key: your_api_key


1. Creating a Sale

To create a POS Sale , use:

POST /v1/sales

A POS sale is a regular sale where:

  • paymentMethods includes Pos
  • paymentMethodOptions.pos.terminalId specifies the target terminal

Required POS-specific fields

At minimum, for a POS sale you should provide:

  • reference
  • currency
  • totalAmount
  • paymentMethods: ["Pos"]
  • paymentMethodOptions.pos.terminalId
  • sequenceType: "OneOff"
  • intentType: "Pay"For a normal POS payment, Pay is the most likely value.

Example request

{
  "storeId": "s_1234abcd",
  "reference": "order-10001",
  "currency": "EUR",
  "totalAmount": "49.95",
  "description": "In-store purchase",
  "locale": "nl-NL",
  "sequenceType": "OneOff",
  "intentType": "Pay",
  "statementDescriptor": "Order #10001",
  "paymentMethods": [
    "Pos"
  ],
  "paymentMethodOptions": {
    "pos": {
      "terminalId": "t_abcd1234efgh5678"
    }
  },
  "metadata": {
    "cashierId": "emp-001",
    "basketId": "basket-789"
  }
}

Successful response

A successful create returns:

  • 201 Created

Response body

{
  "id": "sl_abcd1234efgh5678",
  "storeId": "s_1234abcd",
  "currency": "EUR",
  "totalAmount": "49.95",
  "openAmount": "49.95",
  "paidAmount": "0.00",
  "refundedAmount": "0.00",
  "reversedAmount": "0.00",
  "reference": "order-10001",
  "status": "Open",
  "description": "In-store purchase",
  "locale": "nl-NL",
  "sequenceType": "OneOff",
  "paymentMethods": [
    "Pos"
  ],
  "paymentMethodOptions": {
    "pos": {
      "terminalId": "t_abcd1234efgh5678"
    }
  },
	"metadata": {
    "cashierId": "emp-001",
    "basketId": "basket-789"
  }
  "createdAt": "2024-05-16T10:30:00Z"
}

Common error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 404 Not Found

Example problem response:

{
  "title": "Bad Request",
  "status": 400,
  "detail": "The request is invalid.",
  "errorCode": "VS01"
}

2. Retrieving a Sale

To retrieve a specific sale, use:

GET /v1/sales/{saleId}

Path parameter

  • saleId: the sale identifier

Successful response

A successful retrieval returns:

  • 200 OK

Response body: SaleContract

Example response

{
  "id": "sl_abcd1234efgh5678",
  "storeId": "s_1234abcd",
  "currency": "EUR",
  "totalAmount": "49.95",
  "openAmount": "0.00",
  "paidAmount": "49.95",
  "refundedAmount": "0.00",
  "reversedAmount": "0.00",
  "reference": "order-10001",
  "status": "Paid",
  "description": "In-store purchase",
  "sequenceType": "OneOff",
  "paymentMethods": [
    "Pos"
  ],
  "paymentMethodOptions": {
    "pos": {
      "terminalId": "t_abcd1234efgh5678"
    }
  },
  "transactions": [
    {
      "id": "t_abcd1234efgh5678",
      "storeId": "s_1234abcd",
      "terminalId": "t_abcd1234efgh5678",
      "serviceCode": "POS",
      "intentType": "Pay",
      "collectionType": "Processing",
      "sequenceType": "OneOff",
      "currency": "EUR",
      "amount": "49.95",
      "merchantReference": "order-10001",
      "status": "Successful",
      "saleId": "sl_abcd1234efgh5678",
      "createdAt": "2024-05-16T10:31:00Z"
    }
  ],
  "timeline": [
    {
      "eventLogType": "Sale was paid",
      "eventDateTime": "2024-05-16T10:31:00Z"
    }
  ],
	"metadata": {
    "cashierId": "emp-001",
    "basketId": "basket-789"
  }
  "createdAt": "2024-05-16T10:30:00Z"
}

Useful fields in the response

  • status: current sale status
  • openAmount: remaining unpaid amount
  • paidAmount: amount already paid
  • transactions: underlying POS transaction(s)
  • timeline: lifecycle events for the sale

Possible sale statuses

  • Open
  • Paid
  • Cancelled
  • Failed
  • Expired

Error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 404 Not Found

3. Configuring a Webhook Subscription for Sale Events

Webhooks let your system receive notifications when sale-related events occur.

There are two relevant endpoints:

  • GET /v1/webhooks/events to list available event types
  • POST /v1/webhooks to create a webhook subscription

Step 1: List available event types

GET /v1/webhooks/events

This returns the event types your webhook can subscribe to.

Example response

{
  "eventTypes": [
    {
      "code": "TRANSACTION.CREATE",
      "description": [
        {
          "locale": "en-US",
          "value": "Transaction created"
        }
      ]
    }
  ]
}

The schema allows event type codes as strings. The webhook examples in the spec include sale events such as:

  • SALE.CREATE
  • SALE.UPDATE

Because the request examples use these values, those are the appropriate sale-related event codes to use in your webhook configuration.

Step 2: Create the webhook

POST /v1/webhooks

Request body fields

  • url: destination endpoint
  • storeId: optional, to scope the webhook to a specific store
  • eventTypes: list of event codes
  • maxConcurrency: maximum concurrent deliveries
  • secret: secret used to sign webhook payloads

Example request

{
  "url": "https://example.com/webhooks/sales",
  "storeId": "s_1234abcd",
  "eventTypes": [
    "SALE.CREATE",
    "SALE.UPDATE"
  ],
  "maxConcurrency": 10,
  "secret": "your-webhook-secret"
}

Successful response

A successful create returns:

  • 201 Created

Response body:

{
  "id": "es_1234abcd5678efgh",
  "url": "https://example.com/webhooks/sales",
  "storeId": "s_1234abcd",
  "eventTypes": [
    "SALE.CREATE",
    "SALE.UPDATE"
  ],
  "maxConcurrency": 10,
  "secret": "your-webhook-secret",
  "status": "Active",
  "createdAt": "2024-05-16T00:00:00Z"
}

Webhook status values

  • Active
  • Inactive

Error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 404 Not Found

4. Canceling a Sale

To cancel a sale, use:

POST /v1/sales/{saleId}/cancel

Path parameter

  • saleId: the identifier of the sale to cancel

Example request

POST /v1/sales/sl_abcd1234efgh5678/cancel

This endpoint does not require a request body.

Successful response

A successful cancellation returns:

  • 200 OK

Response body: updated SaleContract

Example response

{
  "id": "sl_abcd1234efgh5678",
  "storeId": "s_1234abcd",
  "currency": "EUR",
  "totalAmount": "49.95",
  "openAmount": "0.00",
  "paidAmount": "0.00",
  "refundedAmount": "0.00",
  "reversedAmount": "0.00",
  "reference": "order-10001",
  "status": "Cancelled",
  "paymentMethods": [
    "Pos"
  ],
  "paymentMethodOptions": {
    "pos": {
      "terminalId": "t_abcd1234efgh5678"
    }
  },
  "createdAt": "2024-05-16T10:30:00Z"
}

When to use this endpoint

Use this endpoint when you want to cancel the sale resource itself.

Related terminal cancel endpoints

The API also exposes terminal-level cancel endpoints:

  • POST /v1/pos/terminals/smart/{terminalId}/cancel
  • POST /v1/pos/terminals/internal/{terminalId}/cancel

These endpoints cancel the current action or terminal transaction at device level, while POST /v1/sales/{saleId}/cancel cancels the sale resource.

If your flow is sale-centric, prefer the sale cancel endpoint. If your flow is terminal-operation-centric, the terminal cancel endpoint may also be relevant.

Error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 404 Not Found

End-to-end example

1. Create POS sale

POST /v1/sales

{
  "reference": "order-10001",
  "currency": "EUR",
  "totalAmount": "49.95",
  "sequenceType": "OneOff",
  "intentType": "Pay",
  "paymentMethods": ["Pos"],
  "paymentMethodOptions": {
    "pos": {
      "terminalId": "t_abcd1234efgh5678"
    }
  }
}

2. Status

2A. Poll sale status

GET /v1/sales/sl_abcd1234efgh5678

Watch for the status field to change to a final value (Paid, Cancelled, Failed or Expired)

2B. Receive sale updates through webhooks

Create a webhook subscribed to:

[
  "SALE.CREATE",
  "SALE.UPDATE"
]

3. Cancel if needed

POST /v1/sales/sl_abcd1234efgh5678/cancel