---
title: "Record Payment"
method: POST
path: "/billing/payments"
tags: ["billing"]
---

# Record Payment

`POST /billing/payments`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/billing/payments`
- Auth: none declared

Record one provider transaction and apply it. **Idempotent on ``external_ref``.**

**200 and not 201, on every outcome including a duplicate.** Webhooks are delivered
at-least-once, and a provider treats anything but a 2xx as "try again" — so a
duplicate answered with a 409 would be redelivered for days, and every redelivery
would be another attempt to grant. `duplicate: true` in the body is how the caller
tells the two apart.

## Headers

- `x-billing-token` string, nullable

## Request body

- PaymentIn: One provider transaction, **already normalised**. Nothing about Payrexx's wire format appears here: the translation is the payment service's whole job, so a second provider is a second translator rather than a second shape in the billing model. That is also what keeps this endpoint reviewable — it has one meaning per field, rather than "whatever that provider happened to send". **The entitlement is named by a CODE, never by a magnitude.** An earlier cut took `monthly_limit` and `units` straight off the wire, so whoever could reach this endpoint chose both what was charged and what was granted, independently. Now the code is looked up in the price list (`services/billing_catalog`) and the amount is recorded for reconciliation only.
  - `provider` 'payrexx'
  - `external_ref` string, required
  - `payer_user_id` string, required
  - `kind` 'subscription' | 'tokens', required
  - `status` 'confirmed' | 'failed' | 'refunded' | 'cancelled', required
  - `amount_cents` integer, required
  - `currency` 'CHF'
  - `occurred_at` string, date-time, nullable
  - `plan_code` string, nullable
  - `pack_code` string, nullable
  - `payload` object, nullable
  - `note` string, nullable

## Response `200`

Successful Response

- PaymentOut: What we did about it. ``duplicate`` is a **success**: it is the expected answer to a webhook retry, and the sender must see a 2xx or the provider will keep redelivering for days.
  - `receipt_id` string, required
  - `duplicate` boolean, required
  - `action` string, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
