# In-store payment: Cloud API

## Overview

The Cloud API lets you drive a Yavin terminal over HTTPS, from any device or server: start a transaction, print or share a receipt, abort or reverse a payment. Choose it when the POS is not on the same network as the terminal, or for web-based POS systems.

## At a glance

|  |  |
| --- | --- |
| Base URL | Production: https://api.yavin.com/api. Sandbox: https://api.sandbox.yavin.com/api |
| Authentication | API key in the Authorization: Bearer YOUR_API_KEY header |
| Naming convention | camelCase for all request payloads, except the Item object which is snake_case |
| Current version | v5 for pos/payment, v4 for all other endpoints |
| Result delivery | Asynchronous: the HTTP response only acknowledges the request, the transaction result arrives on your webhook |

## Endpoints

| Endpoint | Method | Version | Purpose |
| --- | --- | --- | --- |
| /v5/pos/payment | POST | v5 | Start a debit, refund, or debit_and_enrol transaction |
| /v4/pos/print/ | POST | v4 | Print content on the terminal |
| /v4/pos/share-receipt | POST | v4 | Share a receipt by SMS, email or print |
| /v4/pos/abort/ | POST | v4 | Abort the ongoing payment |
| /v4/pos/reversal | POST | v4 | Reverse a recent transaction (less than 16 hours old) |

### Versions

| API version | Last updated |
| --- | --- |
| v5 | 04/05/2026 |
| v4 | 04/05/2026 |
| v1 | 02/05/2022 |

## Before you start

Authentication and headers. Every request must carry your company API key and declare its JSON body. You can find your API key on [my.yavin.com](https://my.yavin.com/) in the API tab. Each company (physical point of sale) has its own API key.

| Header | Value | Required |
| --- | --- | --- |
| Authorization | Bearer YOUR_API_KEY | yes |
| Content-Type | application/json | yes |

> ⚠️ Omitting Content-Type: application/json returns a 400 Bad request even when the payload is valid. Set both headers on every request, including the ones that only carry a serialNumber.

```bash
curl --request POST \
  --url https://api.yavin.com/api/v5/pos/payment \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "amount": 1000,
    "serialNumber": "782996JIS2",
    "transactionType": "debit",
    "vendor": {
      "softwareName": "MyPOS",
      "softwareVersion": "1.0"
    }
  }'
```

Notification window. A Cloud API call asks the Yavin server to send a notification to the terminal. This notification is valid for 5 seconds: if the terminal is offline and does not receive it within that window, the action expires and is not retransmitted when the terminal reconnects. In that case no webhook is ever delivered for the returned transactionId. Always handle this case: implement a client-side timeout (125 seconds classic, 62 seconds kiosk and refunds); past this delay with no webhook, treat the payment as failed, optionally confirm via the polling fallback below, and retry with a new idempotentUuid.

Responses. Every endpoint returns a status field: ok means the request was accepted (payment endpoints) or reached the terminal (v4 endpoints), ko means it did not. Errors come as HTTP 400 with status = ko and a message. The transaction result itself arrives on your webhook as a Transaction object, documented at the end of this page along with the other shared objects.

Webhook delivery. The transaction result is delivered to your webhook when the transaction is done (including failures). Configure your webhook URL in [MyYavin](https://my.yavin.com/): Settings > API > Webhook tab (one URL per company). See [Webhooks Management](https://app.notion.com/p/3bc9a8f4fd9a81b582ace5b88b72adf0) for the payload structure.

_[Illustration]_

- The webhook URL must be HTTPS and must not attempt a redirection

- Yavin expects an HTTP 200 acknowledgement. On failure the webhook is retried at: 1 minute, 10 minutes, 1 hour, 8 hours later. After the retries are exhausted, the webhook is deactivated

> 💡 Polling fallback. If the webhook does not arrive within the expected window (125 s classic, 62 s kiosk and refunds), poll GET https://api.yavin.com/api/v5/transaction/?transactionId={transactionId} on the [Webservices API](https://app.notion.com/p/3bc9a8f4fd9a81529b21d135de85f424) with the transactionId returned synchronously, before retrying with a new idempotentUuid.

Timeouts. A payment has a 120 second window between the moment the request is received by the terminal and a successful card read, split in two phases:

1. Up to 60 seconds for optional screens (tips, reference, review). If this phase times out, the transaction is aborted.

1. Up to 60 seconds for the card read itself.

> 💡 Wait for the webhook up to 125 seconds for classic terminal use, 62 seconds for kiosk use (optional screens disabled) and for refunds.

Idempotency. Send an idempotentUuid on every payment request, one per payment attempt. See [IdempotentUuid Management](https://app.notion.com/p/3bc9a8f4fd9a81d6a3cbfce4939ac0e9).

Currency. The currency is the one configured on the merchant profile in the Yavin database. Supported today: EUR, CHF, GBP.

---

## Endpoints

### Start a debit transaction

`POST https://api.yavin.com/api/v5/pos/payment`

Starts a debit payment on the terminal identified by serialNumber.

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `amount` | Integer | yes |  | Amount in cents, must be positive |
| `serialNumber` | String | yes |  | Serial number of the target terminal. On the back of the device next to the S/N label, or in Yavin Pay under Menu > Settings > About |
| `transactionType` | String | no | debit | Use debit. Values are strict: any unknown value, credit included, is silently processed as a debit |
| `idempotentUuid` | String | no | autogenerated | Unique identifier of the payment attempt, see IdempotentUuid Management. Generate a new UUID for each new attempt |
| `cartId` | String | no | null | Your order number, shown in the MyYavin backoffice |
| `customer` | Customer | no |  | Customer information attached to the transaction |
| `enableGiftScreen` | Boolean | no | null | null (default): the terminal tips configuration applies. A non-null value overrides it for this transaction: true shows the tips screen, false skips it |
| `giftAmount` | Integer | no | 0 | Tip or donation in cents, if selected on the POS side |
| `receiptTicket` | ReceiptTicket | no |  | Receipt content printed with the card ticket |
| `reference` | String | no | null | Waiter or person processing the transaction |
| `vendor` | Vendor | no |  | Your software name and version |
| `acceptedPayment` | AcceptedPayment | no | {"acceptedMediumType":"all"} | Restrict accepted card families |
| `checkoutExternalId` | String | no | null | Merchant-unique checkout ID, required when any external order field below is sent. Use a different value for each transaction |
| `externalTableNumber` | String | no | null | Table number. Only used with checkoutExternalId |
| `externalOrderNumber` | String | no | null | Human-readable order number. Only used with checkoutExternalId |
| `externalOrderId` | String | no | null | Unique technical order ID from your POS. Only used with checkoutExternalId |
| `items` | Array of Item | no |  | Detailed basket lines |

#### Request

_JSON_
```json
{
  "amount": 1000,
  "serialNumber": "782996JIS2",
  "transactionType": "debit",
  "idempotentUuid": "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  },
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  },
  "customer": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@yavin.com"
  },
  "acceptedPayment": {
    "acceptedMediumType": "all"
  },
  "checkoutExternalId": "checkout_123",
  "externalOrderId": "order_uuid_123",
  "externalOrderNumber": "A-1024",
  "externalTableNumber": "12",
  "items": [
    {
      "name": "Menu midi",
      "total_amount": 1590,
      "quantity": 1,
      "tax": { "amount": 265, "rate": 20 }
    }
  ]
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v5/pos/payment' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "amount": 1000,
  "serialNumber": "782996JIS2",
  "transactionType": "debit",
  "idempotentUuid": "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  },
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  },
  "customer": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@yavin.com"
  },
  "acceptedPayment": {
    "acceptedMediumType": "all"
  },
  "checkoutExternalId": "checkout_123",
  "externalOrderId": "order_uuid_123",
  "externalOrderNumber": "A-1024",
  "externalTableNumber": "12",
  "items": [
    {
      "name": "Menu midi",
      "total_amount": 1590,
      "quantity": 1,
      "tax": { "amount": 265, "rate": 20 }
    }
  ]
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v5/pos/payment", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "amount": 1000,
      "serialNumber": "782996JIS2",
      "transactionType": "debit",
      "idempotentUuid": "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
      "vendor": {
        "softwareName": "MyPOS",
        "softwareVersion": "1.0"
      },
      "receiptTicket": {
        "data": "This is the receipt ticket\nto print",
        "format": "text"
      },
      "customer": {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john@yavin.com"
      },
      "acceptedPayment": {
        "acceptedMediumType": "all"
      },
      "checkoutExternalId": "checkout_123",
      "externalOrderId": "order_uuid_123",
      "externalOrderNumber": "A-1024",
      "externalTableNumber": "12",
      "items": [
        {
          "name": "Menu midi",
          "total_amount": 1590,
          "quantity": 1,
          "tax": { "amount": 265, "rate": 20 }
        }
      ]
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "amount": 1000,
  "serialNumber": "782996JIS2",
  "transactionType": "debit",
  "idempotentUuid": "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  },
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  },
  "customer": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@yavin.com"
  },
  "acceptedPayment": {
    "acceptedMediumType": "all"
  },
  "checkoutExternalId": "checkout_123",
  "externalOrderId": "order_uuid_123",
  "externalOrderNumber": "A-1024",
  "externalTableNumber": "12",
  "items": [
    {
      "name": "Menu midi",
      "total_amount": 1590,
      "quantity": 1,
      "tax": { "amount": 265, "rate": 20 }
    }
  ]
}

response = requests.post(
    "https://api.yavin.com/api/v5/pos/payment",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

_JSON_
```json
{
  "status": "ok",
  "transactionId": "dIuiP6NovnZS"
}
```

### Start a transaction and save the card (debit_and_enrol)

`POST https://api.yavin.com/api/v5/pos/payment`

Starts a payment and tokenizes the card in the same flow. amount can be 0 to enrol a card without charging it. The card token is delivered with the transaction result on your webhook.

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `amount` | Integer | yes |  | Amount in cents, 0 or greater |
| `serialNumber` | String | yes |  | Terminal serial number |
| `transactionType` | String | yes |  | Must be debit_and_enrol |
| `idempotentUuid` | String | no | autogenerated | Idempotency key |
| `customer` | Customer | no |  | Customer data |
| `vendor` | Vendor | no |  | Software editor information |
| `reference` | String | no | null | Waiter or person reference |
| `receiptTicket` | ReceiptTicket | no |  | Optional receipt content |
| `receiptTicketJson` | String | no | null | Additional JSON payload as string |
| `acceptedPayment` | AcceptedPayment | no | {"acceptedMediumType":"all"} | Restrict accepted card families |
| `checkoutExternalId, externalOrderId, externalOrderNumber, externalTableNumber` | String | no | null | External order references |
| `items` | Array of Item | no |  | Basket lines |

#### Request

_JSON_
```json
{
  "amount": 0,
  "serialNumber": "782996JIS2",
  "transactionType": "debit_and_enrol",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  }
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v5/pos/payment' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "amount": 0,
  "serialNumber": "782996JIS2",
  "transactionType": "debit_and_enrol",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  }
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v5/pos/payment", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "amount": 0,
      "serialNumber": "782996JIS2",
      "transactionType": "debit_and_enrol",
      "vendor": {
        "softwareName": "MyPOS",
        "softwareVersion": "1.0"
      }
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "amount": 0,
  "serialNumber": "782996JIS2",
  "transactionType": "debit_and_enrol",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  }
}

response = requests.post(
    "https://api.yavin.com/api/v5/pos/payment",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

Same as the debit endpoint: status = ok plus a transactionId on acceptance, status = ko plus a message on failure.

---

### Start a refund transaction

`POST https://api.yavin.com/api/v5/pos/payment`

Credits funds back to the customer card. Same endpoint, with transactionType set to refund. No optional screens, so wait for the webhook up to 62 seconds.

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `transactionType` | String | yes | debit | Must be set to refund. Any other value, such as credit, starts a debit |

#### Request

_JSON_
```json
{
  "amount": 1000,
  "serialNumber": "782996JIS2",
  "transactionType": "refund",
  "idempotentUuid": "8a4d1f0b-2e57-4c1a-b3d9-1c7e5f2a6d40",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  },
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  }
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v5/pos/payment' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "amount": 1000,
  "serialNumber": "782996JIS2",
  "transactionType": "refund",
  "idempotentUuid": "8a4d1f0b-2e57-4c1a-b3d9-1c7e5f2a6d40",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  },
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  }
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v5/pos/payment", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "amount": 1000,
      "serialNumber": "782996JIS2",
      "transactionType": "refund",
      "idempotentUuid": "8a4d1f0b-2e57-4c1a-b3d9-1c7e5f2a6d40",
      "vendor": {
        "softwareName": "MyPOS",
        "softwareVersion": "1.0"
      },
      "receiptTicket": {
        "data": "This is the receipt ticket\nto print",
        "format": "text"
      }
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "amount": 1000,
  "serialNumber": "782996JIS2",
  "transactionType": "refund",
  "idempotentUuid": "8a4d1f0b-2e57-4c1a-b3d9-1c7e5f2a6d40",
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  },
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  }
}

response = requests.post(
    "https://api.yavin.com/api/v5/pos/payment",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

Same as the debit endpoint: status = ok plus a transactionId on acceptance, status = ko plus a message on failure.

---

### Print a receipt ticket

`POST https://api.yavin.com/api/v4/pos/print/`

Prints free content on the terminal (invoice, receipt, kitchen slip).

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `serialNumber` | String | yes |  | Terminal identifier |
| `data` | String | yes | null | Content to print |
| `format` | String | no | text | text or escpos |

#### Request

_JSON_
```json
{
  "serialNumber": "123456789",
  "format": "text",
  "data": "This is a ticket\nto print !"
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v4/pos/print/' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "serialNumber": "123456789",
  "format": "text",
  "data": "This is a ticket\nto print !"
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v4/pos/print/", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "serialNumber": "123456789",
      "format": "text",
      "data": "This is a ticket\nto print !"
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "serialNumber": "123456789",
  "format": "text",
  "data": "This is a ticket\nto print !"
}

response = requests.post(
    "https://api.yavin.com/api/v4/pos/print/",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

200: status = ok (the request reached the terminal) or ko (it did not). 400: status = ko with a message.

---

### Share a receipt ticket

`POST https://api.yavin.com/api/v4/pos/share-receipt`

Shares a receipt by SMS, email or print. The terminal prompts for the recipient information (phone number or email).

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `serialNumber` | String | yes |  | Terminal identifier |
| `receiptTicket` | ReceiptTicket | yes |  | Receipt to share |
| `transactionId` | String | yes |  | Transaction linked to the receipt |
| `medium` | String | yes |  | One of yavin, sms, email, print, in lowercase (SMS is rejected). With yavin, the receipt is stored and shared according to the user notification preferences |

#### Request

_JSON_
```json
{
  "serialNumber": "123456789",
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  },
  "transactionId": "fsOv43g7wxZ",
  "medium": "email"
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v4/pos/share-receipt' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "serialNumber": "123456789",
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  },
  "transactionId": "fsOv43g7wxZ",
  "medium": "email"
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v4/pos/share-receipt", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "serialNumber": "123456789",
      "receiptTicket": {
        "data": "This is the receipt ticket\nto print",
        "format": "text"
      },
      "transactionId": "fsOv43g7wxZ",
      "medium": "email"
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "serialNumber": "123456789",
  "receiptTicket": {
    "data": "This is the receipt ticket\nto print",
    "format": "text"
  },
  "transactionId": "fsOv43g7wxZ",
  "medium": "email"
}

response = requests.post(
    "https://api.yavin.com/api/v4/pos/share-receipt",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

200: status = ok (the request reached the terminal) or ko (it did not). 400: status = ko with a message.

---

### Abort a transaction

`POST https://api.yavin.com/api/v4/pos/abort/`

Aborts the ongoing payment on the terminal: every payment screen is closed and the terminal returns to its main screen.

> ⚠️ Avoid aborting during the card reading phase: the payment gateway screen may close unexpectedly and the payment information may not be transmitted properly.

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `serialNumber` | String | yes |  | Terminal identifier |
| `idempotentUuid` | String | no | null | UUID of the payment attempt to abort |

#### Request

_JSON_
```json
{
  "serialNumber": "123456789",
  "idempotentUuid": "dbcb384c-7d8d-4d2b-b367-133bfdf5c9c"
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v4/pos/abort/' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "serialNumber": "123456789",
  "idempotentUuid": "dbcb384c-7d8d-4d2b-b367-133bfdf5c9c"
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v4/pos/abort/", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "serialNumber": "123456789",
      "idempotentUuid": "dbcb384c-7d8d-4d2b-b367-133bfdf5c9c"
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "serialNumber": "123456789",
  "idempotentUuid": "dbcb384c-7d8d-4d2b-b367-133bfdf5c9c"
}

response = requests.post(
    "https://api.yavin.com/api/v4/pos/abort/",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

200: status = ok (the request reached the terminal) or ko (it did not). 400: status = ko with a message.

---

### Reverse a transaction

`POST https://api.yavin.com/api/v4/pos/reversal`

Reverses a recent transaction.

> ⚠️ Reversal conditions. The original transaction is an ok debit processed on the same terminal issuing the reversal, less than 16 hours ago (terminal time), and the reversal amount is equal to the original total amount, tip included (amount + giftAmount).

#### Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `amount` | Integer | yes |  | Amount in cents, must equal the original total amount (amount  • giftAmount) |
| `initialTransactionId` | String | yes |  | Transaction to reverse |

#### Request

_JSON_
```json
{
  "amount": 1000,
  "initialTransactionId": "dIuiP6NovnZS"
}
```

_cURL_
```bash
curl -X POST 'https://api.yavin.com/api/v4/pos/reversal' \
  -H 'Authorization: Bearer YAVIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "amount": 1000,
  "initialTransactionId": "dIuiP6NovnZS"
}'
```

_Node_
```javascript
(async () => {
  const response = await fetch("https://api.yavin.com/api/v4/pos/reversal", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.YAVIN_API_KEY}`,
    },
    body: JSON.stringify({
      "amount": 1000,
      "initialTransactionId": "dIuiP6NovnZS"
    }),
  });

  if (!response.ok) throw new Error(`Yavin API error ${response.status}`);
  const data = await response.json();
  console.log(data);
})();
```

_Python_
```python
import os
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + os.environ["YAVIN_API_KEY"],
}

payload = {
  "amount": 1000,
  "initialTransactionId": "dIuiP6NovnZS"
}

response = requests.post(
    "https://api.yavin.com/api/v4/pos/reversal",
    headers=headers,
    json=payload,
    timeout=125,
)
response.raise_for_status()
data = response.json()
```

#### Response

200: status = ok or ko, with initialTransactionId and reversalTransactionId. 400: status = ko with a message.

---

## Other

### The Transaction object

Delivered to your webhook once the transaction is done. See [Webhooks Management](https://app.notion.com/p/3bc9a8f4fd9a81b582ace5b88b72adf0) for the full webhook envelope.

```json
{
  "status": "ok",
  "transactionId": "dIuiP6NovnZS",
  "localId": "1042",
  "amount": 1000,
  "giftAmount": 0,
  "currencyCode": "EUR",
  "transactionType": "debit",
  "createdAt": "2026-08-14T10:22:31",
  "appVersion": "3.2.8",
  "serialNumber": "782996JIS2",
  "paymentApplication": "EMV_CONTACTLESS",
  "scheme": "CB",
  "issuer": "VISA",
  "pan": "424242******4242",
  "ticketUrl": "https://t.yavin.com/dIuiP6NovnZS",
  "reference": "Luke",
  "clientTicket": "...",
  "companyTicket": "...",
  "checkoutExternalId": "checkout_123",
  "externalTableNumber": "12",
  "externalOrderNumber": "A-1024",
  "externalOrderId": "order_uuid_123"
}
```

#### Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| status | String | ok, ko, pending |
| transactionId | String | Server-side identifier |
| localId | String | Terminal-side identifier |
| amount | Integer | Amount in cents, excluding giftAmount. The customer is debited the sum of amount and giftAmount (equal to total_amount in the webhook payload) |
| giftAmount | Integer | Tip or donation in cents |
| currencyCode | String | ISO 4217 code |
| transactionType | String | eg debit |
| createdAt | String | Creation date in the Yavin database |
| appVersion | String | Yavin Pay app version |
| serialNumber | String | Terminal identifier |
| clientTicket / companyTicket | String | Client and merchant tickets |
| pan | String | Masked card PAN |
| ticketUrl | String | URL of the digital client ticket |
| paymentApplication | String | AMEX, ANCV, CONECS_CONTACT, CONECS_CONTACTLESS, EMV_CONTACT, EMV_CONTACTLESS, EMV_MOTO, EMV_PAYMENT_LINK, RESTOFLASH, DISCOVER, CUP |
| scheme | String | Acceptance network (eg VISA) |
| issuer | String | Card issuer |
| reference | String | Waiter or person who performed the transaction |
| checkoutExternalId, externalTableNumber, externalOrderNumber, externalOrderId | String | External order references |

---

### The Customer object

Optional on the payment endpoint. Customer information attached to the transaction. It is not used by /share-receipt, where the terminal always prompts for the recipient.

```json
{
  "customer": {
    "firstName": "John",
    "lastName": "Doe",
    "phone": "+33612345678",
    "email": "john@yavin.com"
  }
}
```

#### Attributes

| Attribute | Type | Required | Description |
| --- | --- | --- | --- |
| firstName | String | no | First name |
| lastName | String | no | Last name |
| email | String | no | Email |
| phone | String | no | International format starting with + (eg +33612345678) |

---

### The ReceiptTicket object

Content printed alongside the card ticket on the payment endpoint, or shared on /share-receipt.

```json
{
  "receiptTicket": {
    "data": "Receipt ticket here to print if needed",
    "format": "text"
  }
}
```

#### Attributes

| Attribute | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| data | String | yes |  | Content to print |
| format | String | no | text | Format of the content |

---

### The Vendor object

Identifies your POS software. Optional everywhere, but strongly recommended: it is what lets our support team trace an issue back to a specific integration and version.

```json
{
  "vendor": {
    "softwareName": "MyPOS",
    "softwareVersion": "1.0"
  }
}
```

#### Attributes

| Attribute | Type | Required | Description |
| --- | --- | --- | --- |
| softwareName | String | no | Name of the POS software |
| softwareVersion | String | no | Version of the POS software |

---

### The AcceptedPayment object

Restricts the card families the terminal will accept for this transaction. Useful for a kiosk that must refuse meal vouchers, for example.

```json
{
  "acceptedPayment": {
    "acceptedMediumType": "bank_cards_only"
  }
}
```

#### Attributes

| Attribute | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| acceptedMediumType | String | no | all | all, lunch_vouchers_only, bank_cards_only |

---

### The Item object

Basket lines sent with a payment. Available on v5 pos/payment only. Items drive the meal-voucher eligibility rules, so send them whenever the merchant accepts meal vouchers.

> ⚠️ Item fields are in snake_case, unlike the rest of the payload.

```json
{
  "items": [
    {
      "name": "Menu midi",
      "total_amount": 1590,
      "total_amount_without_tax": 1325,
      "quantity": 1,
      "unit_price": 1590,
      "category": "food",
      "eligible_titre_restaurant": true,
      "tax": { "amount": 265, "rate": 20 }
    }
  ]
}
```

#### Attributes

| Attribute | Type | Required | Description |
| --- | --- | --- | --- |
| name | String | yes | Item name |
| total_amount | Integer | yes | Total amount in cents |
| total_amount_without_tax | Integer | no | Item total before tax |
| category | String | no | Item category |
| eligible_titre_restaurant | Boolean | no | Eligibility for meal voucher |
| free_note | String | no | Free text note |
| quantity | Integer | no | Quantity |
| unit_price | Integer | no | Unit price in cents |
| unit_price_without_tax | Integer | no | Unit price before tax in cents |
| tax | Object | no | { "amount": <int>, "rate": <int> } |
| items | Array | no | Nested items |

---

### Related pages

[Webhooks Management](https://app.notion.com/p/3bc9a8f4fd9a81b582ace5b88b72adf0)

[IdempotentUuid Management](https://app.notion.com/p/3bc9a8f4fd9a81d6a3cbfce4939ac0e9)
