# Payment sessions

> A payment session is one checkout for one amount, hosted on checkout.borga.is or embedded on your page.

Source: https://docs.borga.is/api/payment-sessions

Creating a session creates a Payment and a checkout to collect it. Hosted sessions return a `url` to redirect the payer to; embedded sessions return a `client_secret` for [borga.js](/payments/embedded-checkout). Sessions expire after 24 hours.

**The payment session object**

- `id` (string): Prefixed `ps_`.
- `payment` (string): Id of the Payment this session collects.
- `payment_status` (string): Current status of the payment. Present when retrieving a session.
- `url` (string | null): Hosted checkout URL. `null` for embedded sessions.
- `client_secret` (string | null): Embedded sessions only, and only in the create response. Prefixed `pcs_`.
- `status` (enum): `open`, `complete` or `expired`.
- `mode` (enum): `hosted` or `embedded`. This is the checkout mode, not test or live.
- `enabled_methods` (array): Payment methods offered: `card`, `apple_pay`, `google_pay`, `bank_invoice`.
- `return_url` (string): Where the payer is sent after paying, with `?session=ps_…` appended. Empty for embedded sessions.
- `cancel_url` (string): Where the payer is sent if they go back.
- `locale` (string): Checkout language, `is` or `en`.
- `customer_email` (string | null): Pre-filled payer email, if any.
- `customer_kennitala` (string | null): Pre-filled payer kennitala, if any.
- `expires_at` (timestamp): When the session stops accepting payment.
- `created_at` (timestamp): ## Create a payment session `POST /v1/payment_sessions` (publishable) Either `amount` or `payment` is required. Hosted sessions also require `return_url` and `cancel_url`; embedded sessions created with a secret key require `origin`. Publishable keys may only create embedded sessions, and the browser's `Origin` header must be on the key's allow-list. **Body** <Param name="amount" type="integer">Amount in the smallest currency unit, 1 to 2,000,000,000. Creates a new Payment. Required unless `payment` is set.
- `currency` (string, default ISK): ISO 4217 code. Supported: _(Currencies table: see the HTML page)_
- `payment` (string): Attach to an existing Payment in status `created` instead of creating one.
- `mode` (enum, default hosted): `hosted` redirects to checkout.borga.is; `embedded` returns a `client_secret` for borga.js.
- `return_url` (string): Required for hosted sessions. HTTPS on an allowed redirect domain; `http://localhost` is allowed in test mode.
- `cancel_url` (string): Required for hosted sessions. Same rules as `return_url`.
- `origin` (string): Required for embedded sessions created with a secret key. The `https://host` of the page that opens the modal. Ignored for publishable keys, which use the request's `Origin`.
- `customer` (string): Attach the Payment to a Customer. Required for `save_payment_method` to attach the card somewhere you can find it.
- `customer_email` (string): Pre-fills and locks the payer's email.
- `customer_kennitala` (string): Pre-fills and locks the payer's kennitala for bank invoices. Checksum-validated.
- `external_reference` (string): Your order or invoice reference. Copied to the Payment.
- `metadata` (object): Copied to the Payment.
- `enabled_methods` (array): Add `bank_invoice` to offer krafa (hosted only, PayDay required).
- `locale` (string, default is): Language tag such as `is` or `en`.
- `save_payment_method` (boolean): Tokenise the card after payment and attach it to the customer.
- `subscription` (object): `{ items: [{ price, quantity? }] }`. Starts a subscription with these recurring prices when the payment succeeds. Implies `save_payment_method`.

```ts Node.js
const session = await borga.paymentSessions.create(
  {
    amount: 12900,
    currency: "ISK",
    customer_email: "anna@example.is",
    external_reference: "order_5678",
    return_url: "https://yoursite.is/order/complete",
    cancel_url: "https://yoursite.is/cart",
    enabled_methods: ["card", "apple_pay", "google_pay"],
    locale: "is",
    metadata: { order_id: "5678" },
  },
  // Your own idempotency key makes retries across processes safe too.
  { idempotencyKey: "order_5678_checkout" },
);

// Send the payer to the hosted page.
redirect(session.url!);
```
```bash curl
curl https://api.borga.is/v1/payment_sessions \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order_5678_checkout" \
  -d '{
    "amount": 12900,
    "currency": "ISK",
    "customer_email": "anna@example.is",
    "external_reference": "order_5678",
    "return_url": "https://yoursite.is/order/complete",
    "cancel_url": "https://yoursite.is/cart",
    "enabled_methods": ["card", "apple_pay", "google_pay"],
    "locale": "is",
    "metadata": { "order_id": "5678" }
  }'
```

```json Response
{
  "id": "ps_3kD9mQ2vXb7LpR4tYw8Nz1Ha",
  "payment": "pay_7Hs2Kq9LmW4xZc1Vb8Ny3Rt6",
  "url": "https://checkout.borga.is/ps_3kD9mQ2vXb7LpR4tYw8Nz1Ha",
  "return_url": "https://example.is/order/complete",
  "cancel_url": "https://example.is/cart",
  "expires_at": "2026-09-08T12:00:00.000Z",
  "status": "open",
  "enabled_methods": ["card", "apple_pay", "google_pay"],
  "mode": "hosted",
  "locale": "is",
  "customer_email": null,
  "customer_kennitala": null,
  "created_at": "2026-09-07T12:00:00.000Z",
  "client_secret": null
}
```

Errors: [`missing_amount`](/errors/missing_amount), [`missing_redirect_urls`](/errors/missing_redirect_urls), [`redirect_url_not_allowed`](/errors/redirect_url_not_allowed), [`missing_origin`](/errors/missing_origin), [`origin_not_allowed`](/errors/origin_not_allowed), [`publishable_key_not_allowed`](/errors/publishable_key_not_allowed), [`bank_invoice_not_enabled`](/errors/bank_invoice_not_enabled), [`invalid_kennitala`](/errors/invalid_kennitala), [`resource_not_found`](/errors/resource_not_found).

## Retrieve a payment session

`GET /v1/payment_sessions/:id`

Returns the session with `payment_status`, which saves a second call on your return page. `client_secret` is never returned here.

```bash
curl https://api.borga.is/v1/payment_sessions/ps_3kD9mQ2vXb7LpR4tYw8Nz1Ha \
  -H "Authorization: Bearer sk_test_…"
```

There is no list endpoint for sessions and no way to cancel one early; unpaid sessions expire on their own.
