customers

Stable. Describes behaviour that is built and covered by tests in this repository.

API reference

GET /v1/customers #

List customers (ordered by external_id)

Parameters

NameInTypeDescription
page_sizequeryinteger
page_tokenquerystring

Responses

StatusMeaningBody
200OKListCustomersResponse
400Error envelopeError
Example response
{
  "customers": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "external_id": "string",
      "name": "string",
      "email": "string",
      "payment_method": "string",
      "create_time": "2026-11-03T10:15:00Z",
      "country": "string",
      "billing_currency": "string",
      "address": {
        "line1": "string",
        "line2": "string",
        "city": "string",
        "region": "string",
        "postal_code": "string",
        "country": "string"
      },
      "tax_id": "string",
      "tax_office": "string",
      "e_invoice_registered": false,
      "locale": "string",
      "customer_type": "string",
      "block_reason": "string",
      "block_category": "string",
      "service_suspended": false,
      "phone": "string",
      "tax_id_warning": "string"
    }
  ],
  "next_page_token": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/customers #

Create a customer

Request body

FieldTypeDescription
external_id requiredstring
name requiredstring
email requiredstring · email
payment_methodstring
countrystringISO 3166-1 alpha-2
billing_currencystringISO 4217; default is the tenant's currency
addressAddressInputA postal address as printed on an invoice. Every part is optional.
localeen | tr | de | fr | es | arThe language of the customer's mails, receipts and payment pages
customer_typeindividual | businessA person or a taxpayer
phonestringThe customer's mobile number in international form, e.g. +90 532 123 45 67 or 0090 532 123 45 67; stored as E.164 (+905321234567). A number without the + or 00 prefix is refused: it cannot be read without a country.
referral_codestringThe code of the partner whose link or code brought this customer (phase 47). A code that cannot attribute does not fail the creation.
referral_touch_timestring · date-timeWhen the visitor followed that link; default now
Example
{
  "external_id": "string",
  "name": "string",
  "email": "string"
}

Responses

StatusMeaningBody
201CreatedCustomer
400Error envelopeError
409Error envelopeError
429Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"string","name":"string","email":"string"}'

GET /v1/customers/{external_id} #

Get a customer by external id

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKCustomer
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

PUT /v1/customers/{external_id}/payment-method #

Set the customer's PSP token (sandbox accepts only pm_sandbox_* tokens)

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
payment_method requiredstring
Example
{
  "payment_method": "string"
}

Responses

StatusMeaningBody
200OKCustomer
400Error envelopeError
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X PUT https://YOUR-API-ADDRESS/v1/customers/<external_id>/payment-method \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payment_method":"string"}'

POST /v1/customers/{external_id}/payment-method-setup #

Make the link to the provider's page where the customer saves a card for later charges

For a provider that has the customer save a payment method on its own page (Stripe: Checkout in setup mode). The answer is the address of that page; you send it to the customer. Nothing is charged. When the customer has saved the card, the provider tells Payrudder and the method becomes the customer's payment method (the string a charge takes, cus_...:pm_... at Stripe), which later invoices are charged with: there is nothing to call afterwards. The customer is made at the provider once and remembered, so asking again for the same customer reuses it. Payrudder collects and keeps no consent in this flow. The provider's guide asks you to collect the customer's explicit consent to be charged without them present (the amount and timing, how the amount is decided, how to cancel) and to keep a record of it: send the link together with your terms and keep the agreement yourself. The page is made in the environment of the key (a sk_test_ Stripe account for a sandbox key). return_url must be https. 404 payments.customer.not_ready for a customer that does not exist, 501 for a provider that cannot do this, and the provider's own refusal when the environment has no account for it.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
providerstripeThe provider whose page it is; only Stripe has one
return_url requiredstring · uriWhere the provider sends the customer after the page. `https` only
Example
{
  "return_url": "string"
}

Responses

StatusMeaningBody
201CreatedPaymentMethodSetup
400Error envelopeError
404Error envelopeError
501Error envelopeError
502Error envelopeError
503Error envelopeError
Example response
{
  "url": "string",
  "ref": "string"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/payment-method-setup \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"return_url":"string"}'

GET /v1/customers/{external_id}/cards #

The cards the payment provider holds for the customer

The cards of the provider's card storage (iyzico) under the user key the customer's payment method names, never a number: the bank, the first digits (BIN), the last four, the expiry. The card the customer's invoices are charged with is in_use. A customer with no stored card has an empty list and no provider. Payrudder cannot put a card in storage (it holds no card numbers): whoever holds the number puts it there and sets the payment method with PUT /v1/customers/{external_id}/payment-method. The sandbox has none.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKCustomerCards
404Error envelopeError
502Error envelopeError
Example response
{
  "provider": "string",
  "cards": [
    {
      "token": "string",
      "alias": "string",
      "bin": "string",
      "last4": "string",
      "type": "string",
      "association": "string",
      "family": "string",
      "bank": "string",
      "expire_month": "string",
      "expire_year": "string",
      "in_use": false
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/cards \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

DELETE /v1/customers/{external_id}/cards/{token} #

Delete one of the customer's stored cards at the provider

The card must be one of the customer's own (a token of another customer is 404 payments.card.not_found, and nothing is sent to the provider). The customer's payment method is not changed: when the card was in_use the answer says so, and the next charge fails until a new payment method is set.

Parameters

NameInTypeDescription
external_id requiredpathstring
token requiredpathstring

Responses

StatusMeaningBody
200DeletedDeletedCustomerCard
404Error envelopeError
502Error envelopeError
Example response
{
  "provider": "string",
  "deleted": false,
  "was_in_use": false
}
curl
curl -X DELETE https://YOUR-API-ADDRESS/v1/customers/<external_id>/cards/<token> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

PUT /v1/customers/{external_id}/address #

Replace the customer's billing address

The address is printed on the customer's invoices (PDF and hosted page). Its country becomes the customer's country, which provider routing and tax read. An empty object clears the address and keeps the country.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
line1string
line2string
citystring
regionstringState, province or county
postal_codestring
countrystringISO 3166-1 alpha-2
Example
{}

Responses

StatusMeaningBody
200OKCustomer
400Error envelopeError
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X PUT https://YOUR-API-ADDRESS/v1/customers/<external_id>/address \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

PUT /v1/customers/{external_id}/contact #

Replace the customer's name and e-mail address

Invoices, receipts and dunning mails carry the name and go to the e-mail address, in the customer's language. Name and e-mail are required, with the limits of customer creation. Language, kind and phone are optional; omit one to keep what the customer has (only the phone can be cleared, with an empty string). The external id cannot change.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
name requiredstring
email requiredstring · email
localeen | tr | de | fr | es | arThe language of the customer's mails, receipts and payment pages; omit to keep it
customer_typeindividual | businessA person or a taxpayer; omit to keep it
phonestringThe customer's mobile number in international form (+90 532 123 45 67 or 0090 532 123 45 67), stored as E.164. Omit to keep the stored number; send an empty string to clear it.
Example
{
  "name": "string",
  "email": "string"
}

Responses

StatusMeaningBody
200OKCustomer
400Error envelopeError
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X PUT https://YOUR-API-ADDRESS/v1/customers/<external_id>/contact \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"string","email":"string"}'

POST /v1/customers/{external_id}/block #

Block a customer from new purchases

A blocked customer cannot get a new subscription or plan change (error key plans.customer.blocked), buy through a payment link or the portal, or have a payment link made for them (billing.customer.blocked); what exists runs on until you end it, and an invoice that was issued before stays payable, except after a fraud or chargeback block. A price change you schedule skips them (customer_blocked). Blocking again keeps the block that is in force. The note is yours, up to 500 characters; category says why; blocked_until ends the block by itself (it must be in the future, and every check reads it, so the block stops holding at that time). Every block and lift is kept in the customer's history and sent as the customer.blocked / customer.unblocked webhook events (the end of a block by itself sends none). Usage ingestion is not affected, and entitlements are unless you set suspend_service: then every entitlement check of this customer answers allowed: false and suspended: true (the limits and the counted usage are untouched, and the events you send are still accepted and counted) until you lift the block or it ends by itself. The service is suspended only when the block is new: a block already in force is kept as it is, so lift it first to change it.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
reasonstringYour note on why; never shown to the customer.
categoryCustomerBlockCategoryWhy the customer is blocked. fraud and chargeback also refuse paying an invoice that is open.
blocked_untilstring · date-timeWhen the block ends by itself; must be in the future. Absent: until you lift it.
suspend_servicebooleanAlso suspend the customer's service: every entitlement check answers allowed false (suspended true) until the block is lifted or ends. Usage is still accepted and counted. Default false.
Example
{}

Responses

StatusMeaningBody
200OKCustomer
400Error envelopeError
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/block \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

POST /v1/customers/{external_id}/unblock #

Lift a customer's block

Also clears a block that has ended by itself. A customer who was not blocked is returned as it is.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKCustomer
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/unblock \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/block-events #

A customer's blocks and lifts, newest first

Who blocked or lifted, when, and why (the latest 100). Never changed or deleted.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKListCustomerBlockEventsResponse
404Error envelopeError
Example response
{
  "events": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "action": "blocked",
      "category": "string",
      "reason": "string",
      "suspend_service": false,
      "actor": "string",
      "create_time": "2026-11-03T10:15:00Z"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/block-events \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/appeals #

A blocked customer's appeals, newest first

The statements your customer sent from their portal link against a block (the latest 20), each with its status (open, accepted, rejected, or closed when the block was lifted some other way while it was open) and your answer. The statement is the customer's own words: treat it as untrusted text, never as markup. The category and the note of your block are never part of an appeal. Needs customers:read.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKListCustomerBlockAppealsResponse
404Error envelopeError
Example response
{
  "appeals": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "status": "open",
      "statement": "string",
      "answer": "string",
      "decided_by": "string",
      "create_time": "2026-11-03T10:15:00Z"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/appeals \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/customers/{external_id}/appeals/{id}/decision #

Accept or reject a blocked customer's appeal

accepted lifts the block exactly as POST /v1/customers/{external_id}/unblock does (the history row, the customer.unblocked webhook event and, when you have the mail on, the customer's notice) and records your decision in the same step. rejected needs an answer (up to 1000 characters); the block stays and the customer sees your answer on their portal and may send another appeal. An appeal that was decided or closed is refused with plans.customer_appeal.not_open. Both decisions are audit entries (customer_block.appeal_decided); no webhook event is sent for a submitted appeal or a rejection. Needs customers:write.

Parameters

NameInTypeDescription
external_id requiredpathstring
id requiredpathstring · uuid

Request body

FieldTypeDescription
decision requiredaccepted | rejected
answerstringRequired to reject
Example
{
  "decision": "accepted"
}

Responses

StatusMeaningBody
200OKCustomerBlockAppeal
400Error envelopeError
404Error envelopeError
409Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "status": "open",
  "statement": "string",
  "answer": "string",
  "decided_by": "string",
  "create_time": "2026-11-03T10:15:00Z"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/appeals/<id>/decision \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision":"accepted"}'

PUT /v1/customers/{external_id}/tax #

Replace the customer's tax data used on e-invoices

Tax id (VKN/TCKN in Turkey, VAT id in the EU), tax office (Turkey) and whether the customer is registered for e-fatura. Empty values clear them. An id that does not look right for the customer's country and kind is stored all the same: the customer comes back with tax_id_warning set, and tax_id_validated_at is set only for an id that passes the local checksum or shape check. Error key plans.customer.tax_invalid (only for an id that is not 5 to 20 letters and digits, or a tax office that is not one line).

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
tax_idstringVKN/TCKN (Turkey) or VAT id (EU)
tax_officestringVergi dairesi (Turkey)
e_invoice_registeredbooleanThe customer is registered for e-fatura
Example
{}

Responses

StatusMeaningBody
200OKCustomer
400Error envelopeError
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "external_id": "string",
  "name": "string",
  "email": "string",
  "payment_method": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "country": "string",
  "billing_currency": "string",
  "address": {
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postal_code": "string",
    "country": "string"
  },
  "tax_id": "string",
  "tax_office": "string",
  "e_invoice_registered": false,
  "locale": "string",
  "customer_type": "string",
  "block_reason": "string",
  "block_category": "string",
  "service_suspended": false,
  "phone": "string",
  "tax_id_warning": "string"
}
curl
curl -X PUT https://YOUR-API-ADDRESS/v1/customers/<external_id>/tax \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

GET /v1/customers/{external_id}/contract-requirements #

What the customer must accept before a subscription starts, with the text to show

For an embedded checkout. customer_type overrides the customer's own (a business customer gets the business texts; an unknown type is treated as a consumer). The order fills the variables of the texts.

Parameters

NameInTypeDescription
external_id requiredpathstring
localequerystring
customer_typequeryindividual | business
order_descriptionquerystring
order_totalquerystring
order_currencyquerystring

Responses

StatusMeaningBody
200OKGetContractRequirementsResponse
404Error envelopeError
Example response
{
  "enforced": false,
  "requirements": [
    {
      "kind": "distance_sales",
      "version_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "version_number": 1,
      "audience": "consumer",
      "locale": "string",
      "body_html": "string",
      "body_sha256": "string",
      "accepted": false,
      "accept_time": "2026-11-03T10:15:00Z",
      "has_template_notice": false
    }
  ],
  "satisfied": false,
  "immediate_start": false,
  "missing_kinds": [
    "distance_sales"
  ],
  "audience": "consumer"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/contract-requirements \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/contract-acceptances #

What the customer accepted, newest first

The customer's acceptance log, a page at a time (page_size, default 100, at most 200; the next page is next_page_token, empty on the last). The filters narrow it and combine: kind, source (api, hosted), version_id (one version of a text) and the time the customer accepted, from (inclusive) to to (exclusive), as RFC 3339 times.

Parameters

NameInTypeDescription
external_id requiredpathstring
kindquerydistance_sales | pre_info | tos | privacy
sourcequeryapi | hosted
version_idquerystring · uuid
fromquerystring · date-timeAccepted at or after this time
toquerystring · date-timeAccepted before this time
page_sizequeryinteger
page_tokenquerystring

Responses

StatusMeaningBody
200OKListContractAcceptancesResponse
400Error envelopeError
404Error envelopeError
Example response
{
  "acceptances": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "group_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "customer_id": "string",
      "version_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "kind": "distance_sales",
      "audience": "consumer",
      "version_number": 1,
      "version_sha256": "string",
      "locale_requested": "string",
      "locale_served": "string",
      "body_sha256": "string",
      "context_kind": "hosted_invoice",
      "context_ref": "string",
      "source": "hosted",
      "observed": {},
      "checkbox_text": "string",
      "immediate_start_consent": false,
      "immediate_start_text": "string",
      "withdrawal_waiver_acknowledged": false,
      "withdrawal_waiver_text": "string",
      "variables": {},
      "snapshot_sha256": "string",
      "ip": "string",
      "user_agent": "string",
      "accept_time": "2026-11-03T10:15:00Z",
      "customer_external_id": "string"
    }
  ],
  "next_page_token": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/contract-acceptances \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/customers/{external_id}/contract-acceptances #

Record that the customer accepted the current versions (an embedded checkout)

The evidence of one checkout moment: one row for each version, all in one group. Every version must be the current one of its series (plans.contract.version_stale otherwise) and checkbox_text is the words next to the box as the customer saw them. What you report about the customer's IP address, browser and time is your claim, and the record says so (observed is false for those fields); Payrudder's own payment pages record what they observed. Acceptances are append-only. The customer is mailed the page they accepted, in their language. Needs customers:write.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
localestringThe language the page was shown in.
context_refstringYour reference for the checkout (an order id).
items requiredarray of ContractAcceptanceItem
immediate_start_consentboolean
immediate_start_textstring
withdrawal_waiver_acknowledgedboolean
withdrawal_waiver_textstring
ipstringYour claim about the customer's address.
user_agentstringYour claim about the customer's browser.
accept_timestring · date-timeWhen the customer accepted; empty is now.
customer_typeindividual | business
orderContractOrder
Example
{
  "items": [
    {
      "version_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "checkbox_text": "string"
    }
  ]
}

Responses

StatusMeaningBody
201CreatedRecordContractAcceptanceResponse
400Error envelopeError
404Error envelopeError
409Error envelopeError
Example response
{
  "group_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "acceptances": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "group_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "customer_id": "string",
      "version_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "kind": "distance_sales",
      "audience": "consumer",
      "version_number": 1,
      "version_sha256": "string",
      "locale_requested": "string",
      "locale_served": "string",
      "body_sha256": "string",
      "context_kind": "hosted_invoice",
      "context_ref": "string",
      "source": "hosted",
      "observed": {},
      "checkbox_text": "string",
      "immediate_start_consent": false,
      "immediate_start_text": "string",
      "withdrawal_waiver_acknowledged": false,
      "withdrawal_waiver_text": "string",
      "variables": {},
      "snapshot_sha256": "string",
      "ip": "string",
      "user_agent": "string",
      "accept_time": "2026-11-03T10:15:00Z",
      "customer_external_id": "string"
    }
  ]
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/contract-acceptances \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"version_id":"0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e","checkbox_text":"string"}]}'

POST /v1/customers/{external_id}/contract-withdrawals #

Record that the customer withdrew from a contract

The seller decides and refunds; this keeps the statement, when it arrived and who decided what. Sends the webhook event contract.withdrawal_requested. The refund follows the seller's decision, never the statement: see setContractWithdrawalStatus and requestContractWithdrawalRefund. Needs customers:write.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
subscription_idstring · uuid
invoice_idstring · uuid
statementstring
receive_timestring · date-timeWhen the statement reached you; empty is now.
sourceapi | console
Example
{}

Responses

StatusMeaningBody
201CreatedContractWithdrawal
400Error envelopeError
404Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "customer_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "customer_external_id": "string",
  "subscription_id": "string",
  "invoice_id": "string",
  "statement": "string",
  "source": "api",
  "status": "received",
  "receive_time": "2026-11-03T10:15:00Z",
  "decide_time": "2026-11-03T10:15:00Z",
  "decided_by": "string",
  "decision_note": "string",
  "credit_note_id": "string",
  "refund_state": "none",
  "refund_note": "string",
  "refund_time": "2026-11-03T10:15:00Z"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/contract-withdrawals \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

GET /v1/customers/{external_id}/usage #

On-time usage of a customer in a period, per meter

Quantities are exact decimals with the meter's scale. Usage that arrived after the period's cutoff is not included here: it is billed on a later invoice as late usage. period defaults to the current period in the tenant's time zone.

Parameters

NameInTypeDescription
external_id requiredpathstring
periodquerystring

Responses

StatusMeaningBody
200OKGetCustomerUsageResponse
400Error envelopeError
404Error envelopeError
429Error envelopeError
Example response
{
  "customer_external_id": "string",
  "period_id": "string",
  "usage": [
    {
      "meter_code": "string",
      "quantity": "1500.250",
      "event_count": "string"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/usage \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/upcoming-invoice #

What the customer's next invoice would be if it were drafted now

An estimate of the regular invoice of the period that is still open (or, while a closed period's grace period runs, of that period): the usage and late usage so far, rated by the plan version in force, the fixed fee, tax at the tenant's rate for the customer's country and the credit balance the invoice would use. It is computed by the same code that drafts the real invoice, from the same inputs, and nothing is stored. It moves with every event until the period closes, and a change of plan, a coupon or a tax rate made later can move it too: estimate is always true, and the response has no id and no number. A fixed fee invoiced in advance is its own invoice and is not in it. A customer with nothing to bill in the period gets has_invoice: false and no lines.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKUpcomingInvoice
404Error envelopeError
429Error envelopeError
Example response
{
  "estimate": false,
  "customer_external_id": "string",
  "period_id": "2026-11",
  "period_start": "2026-11-03T10:15:00Z",
  "period_end": "2026-11-03T10:15:00Z",
  "currency": "string",
  "lines": [
    {
      "line_no": 1,
      "kind": "BASE_FEE",
      "meter_code": "string",
      "origin_period_id": "string",
      "quantity": "1.250",
      "amount_minor": "string",
      "description": "string",
      "service_from": "2026-11-03T10:15:00Z",
      "service_to": "2026-11-03T10:15:00Z"
    }
  ],
  "subtotal_minor": "string",
  "tax_minor": "string",
  "credit_minor": "string",
  "total_minor": "string",
  "credit_surplus_minor": "string",
  "as_of": "2026-11-03T10:15:00Z",
  "has_invoice": false
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/upcoming-invoice \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/entitlements #

Usage against the limits of every meter the customer has an entitlement on

See getEntitlement for the semantics of each item. Not paged: the response is the complete list.

Parameters

NameInTypeDescription
external_id requiredpathstring
periodquerystring

Responses

StatusMeaningBody
200OKListEntitlementsResponse
400Error envelopeError
404Error envelopeError
429Error envelopeError
Example response
{
  "items": [
    {
      "customer_external_id": "string",
      "meter_code": "string",
      "period_id": "2026-11",
      "used": "string",
      "limit": null,
      "unlimited": false,
      "allowed": false,
      "as_of": null,
      "window": "hour",
      "window_id": "2026-11-05",
      "resets_at": null,
      "mode": "hard",
      "over_limit": false,
      "overridden": false,
      "suspended": false
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/entitlements \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/entitlements/{meter} #

Usage of one meter against the customer's limit

Answered from near-real-time counters. They are eventually consistent (typically well under 2 s behind ingest): as_of is the ingest time of the newest event reflected in used. Payrudder never blocks ingest on a limit; call this before serving your customer and decide what to do. A customer without an entitlement on the meter is unlimited. period defaults to the current period in the tenant's time zone.

A limit counts over its window: the billing month, or an hour, a day, an ISO week, a calendar year or the customer's whole lifetime, in the tenant's time zone. period picks the month of a monthly limit; every other window always reports its current one (window_id, ending at resets_at, which a lifetime window does not have). allowed follows the limit's mode: a hard limit stops allowing at the limit, a soft or notify one keeps allowing and says over_limit. Every window's counter is reconciled against the aggregator every few minutes, so an event delivered twice stops counting twice within one cycle.

Parameters

NameInTypeDescription
external_id requiredpathstring
meter requiredpathstring
periodquerystring

Responses

StatusMeaningBody
200OKEntitlementStatus
400Error envelopeError
404Error envelopeError
429Error envelopeError
Example response
{
  "customer_external_id": "string",
  "meter_code": "string",
  "period_id": "2026-11",
  "used": "string",
  "limit": null,
  "unlimited": false,
  "allowed": false,
  "as_of": null,
  "window": "hour",
  "window_id": "2026-11-05",
  "resets_at": null,
  "mode": "hard",
  "over_limit": false,
  "overridden": false,
  "suspended": false
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/entitlements/<meter> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/entitlement-overrides #

The customer's own limits, which replace its package's on their meters

Not paged: the response is the complete list.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKListEntitlementOverridesResponse
404Error envelopeError
Example response
{
  "overrides": [
    {
      "customer_external_id": "string",
      "meter_code": "string",
      "create_time": "2026-11-03T10:15:00Z",
      "update_time": "2026-11-03T10:15:00Z",
      "limit": "string",
      "alert_thresholds_pct": [
        50,
        80,
        100
      ],
      "window": "QUOTA_WINDOW_UNSPECIFIED",
      "mode": "QUOTA_MODE_UNSPECIFIED"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/entitlement-overrides \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

PUT /v1/customers/{external_id}/entitlement-overrides/{meter} #

Give the customer its own limit on a meter

Replaces the entitlement the customer's package gives on the meter, or adds one on a meter the package does not limit, for every subscription of the customer until the override is deleted. limits-svc applies it within seconds. QUOTA_MODE_SOFT needs every current package of the customer to price the meter (the usage over the limit is billed at that price).

Parameters

NameInTypeDescription
external_id requiredpathstring
meter requiredpathstring

Request body

FieldTypeDescription
limit requiredstringDecimal in meter units; "0" = unlimited
alert_thresholds_pctarray of integerStrictly ascending percentages of the limit at which an alert fires; empty = 80 and 100
windowQuotaWindowWhat a limit counts over, in the tenant's time zone. Unspecified is the month (the billing period). A year is the calendar year (twelve billing periods); a lifetime limit never starts again.
modeQuotaModeWhat reaching the limit means for `allowed` (Payrudder never refuses usage events). HARD (also unspecified): allowed turns false at the limit. SOFT: allowed stays true and the usage over the limit is billed at the meter's price, which the package must have. NOTIFY: allowed stays true; only the alerts are sent.
Example
{
  "limit": "string"
}

Responses

StatusMeaningBody
200OKEntitlementOverride
400Error envelopeError
404Error envelopeError
Example response
{
  "customer_external_id": "string",
  "meter_code": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "update_time": "2026-11-03T10:15:00Z",
  "limit": "string",
  "alert_thresholds_pct": [
    50,
    80,
    100
  ],
  "window": "QUOTA_WINDOW_UNSPECIFIED",
  "mode": "QUOTA_MODE_UNSPECIFIED"
}
curl
curl -X PUT https://YOUR-API-ADDRESS/v1/customers/<external_id>/entitlement-overrides/<meter> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit":"string"}'

DELETE /v1/customers/{external_id}/entitlement-overrides/{meter} #

Return the customer to its package's limit on the meter

Parameters

NameInTypeDescription
external_id requiredpathstring
meter requiredpathstring

Responses

StatusMeaningBody
204Deleted
404Error envelopeError
curl
curl -X DELETE https://YOUR-API-ADDRESS/v1/customers/<external_id>/entitlement-overrides/<meter> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/customers/{external_id}/credits #

What a customer can spend now

The balance leaves out credits whose time has passed. lots shows where it comes from, the credits that expire first at the top.

Parameters

NameInTypeDescription
external_id requiredpathstring

Responses

StatusMeaningBody
200OKCreditBalance
404Error envelopeError
Example response
{
  "balance": "string",
  "next_expire_time": "2026-11-03T10:15:00Z",
  "lots": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "granted": "string",
      "remaining": "string",
      "source": "purchase",
      "expire_time": "2026-11-03T10:15:00Z",
      "create_time": "2026-11-03T10:15:00Z"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/credits \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/customers/{external_id}/credits/grant #

Add credits to a customer

For a promotion, a goodwill gesture or a sale made outside Payrudder. With an idempotency_key (or the Idempotency-Key header) a repeat of the call adds nothing and returns replayed: true; the same key with other values is billing.credits.key_reused.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
credits requiredstring · int64int64, above zero (a number is accepted too)
source requiredpromo | manual
expire_timestring · date-timeMust lie in the future; leave out for credits that never expire
reasonstring
idempotency_keystring
Example
{
  "credits": "string",
  "source": "promo"
}

Responses

StatusMeaningBody
200OKCreditResult
400Error envelopeError
404Error envelopeError
409Error envelopeError
Example response
{
  "balance": "string",
  "replayed": false
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/credits/grant \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"credits":"string","source":"promo"}'

POST /v1/customers/{external_id}/credits/consume #

Spend credits

Takes the credits that expire first. With too few credits nothing is taken and the answer is 409 billing.credits.insufficient. An idempotency_key (or the Idempotency-Key header) is required: a retried call returns the first result with replayed: true instead of charging twice.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
credits requiredstring · int64int64, above zero (a number is accepted too)
reasonstring
idempotency_key requiredstringRequired, here or in the Idempotency-Key header
Example
{
  "credits": "string",
  "idempotency_key": "string"
}

Responses

StatusMeaningBody
200OKCreditResult
400Error envelopeError
404Error envelopeError
409Error envelopeError
Example response
{
  "balance": "string",
  "replayed": false
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/credits/consume \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"credits":"string","idempotency_key":"string"}'

GET /v1/customers/{external_id}/credits/ledger #

Every change to a customer's credits, newest first

Parameters

NameInTypeDescription
external_id requiredpathstring
page_sizequeryinteger
page_tokenquerystring

Responses

StatusMeaningBody
200OKCreditLedgerPage
400Error envelopeError
404Error envelopeError
Example response
{
  "entries": [
    {
      "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "lot_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "kind": "grant",
      "amount": "string",
      "reason": "string",
      "idempotency_key": "string",
      "source": "purchase",
      "create_time": "2026-11-03T10:15:00Z"
    }
  ],
  "next_page_token": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/credits/ledger \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/customers/{external_id}/coupons #

Give a customer a coupon that their next eligible checkout uses without a code

One grant per (customer, source, source_ref); repeating it grants nothing more.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
coupon_code requiredstring
source requiredstringWho gives it, e.g. operator or affiliate
source_ref requiredstring
Example
{
  "coupon_code": "string",
  "source": "string",
  "source_ref": "string"
}

Responses

StatusMeaningBody
200OKGrantCustomerCouponResponse
400Error envelopeError
404Error envelopeError
Example response
{
  "granted": false
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/coupons \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"coupon_code":"string","source":"string","source_ref":"string"}'

POST /v1/customers/{external_id}/referral #

Say which partner brought a customer

For your own sign-up, which reads the ?ref= of the link. First touch wins: a customer who has an attribution keeps it. A refusal is a normal answer (attributed false and a reason), not an error: already_attributed, unknown_code, program_inactive, affiliate_inactive, expired, same_email, same_domain, same_payment_method.

Parameters

NameInTypeDescription
external_id requiredpathstring

Request body

FieldTypeDescription
code requiredstring
sourcelink | code | api
touch_timestring · date-timeWhen the visitor followed the link; default now
Example
{
  "code": "string"
}

Responses

StatusMeaningBody
200OKAttributeCustomerResponse
400Error envelopeError
404Error envelopeError
Example response
{
  "attributed": false,
  "reason": "string"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/customers/<external_id>/referral \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code":"string"}'

GET /v1/customers/{external_id}/payments #

The customer's invoices and the payments recorded for them

The same rows the customer sees on their payment page, newest first (charges and refunds from the ledger; an invoice may have several attempts).

Parameters

NameInTypeDescription
external_id requiredpathstring
limitqueryintegerHow many invoices to read; there is no page_token, so a customer with more invoices cannot be listed completely

Responses

StatusMeaningBody
200OKListCustomerPaymentsResponse
404Error envelopeError
503Error envelopeError
Example response
{
  "invoices": [
    {
      "id": "string",
      "customer_id": "string",
      "customer_external_id": "string",
      "period_id": "string",
      "kind": "REGULAR",
      "number": "INV-2026-000123",
      "currency": "string",
      "subtotal_minor": "string",
      "total_minor": "string",
      "status": "draft",
      "saga_state": "string",
      "create_time": "2026-11-03T10:15:00Z",
      "finalize_time": "2026-11-03T10:15:00Z",
      "lines": [
        {
          "line_no": 1,
          "kind": "BASE_FEE",
          "meter_code": "string",
          "origin_period_id": "string",
          "quantity": "1.250",
          "amount_minor": "string",
          "description": "string",
          "service_from": "2026-11-03T10:15:00Z",
          "service_to": "2026-11-03T10:15:00Z"
        }
      ],
      "payments": [
        {
          "id": "string",
          "invoice_id": "string",
          "method": "bank_transfer",
          "amount_minor": "string",
          "currency": "string",
          "received_at": "2026-11-03T10:15:00Z",
          "reference": "string",
          "note": "string",
          "recorded_by": "string",
          "recorded_at": "2026-11-03T10:15:00Z"
        }
      ],
      "plan_version_id": "string",
      "pdf_object_key": "string",
      "credit_surplus_minor": "string"
    }
  ],
  "payments": [
    {
      "invoice_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
      "invoice_number": "string",
      "kind": "charge",
      "status": "succeeded",
      "amount_minor": "string",
      "currency": "string",
      "provider": "string",
      "environment": "sandbox",
      "occurred_at": "2026-11-03T10:15:00Z"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/customers/<external_id>/payments \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

Models

Address

FieldTypeDescription
line1 requiredstring
line2 requiredstring
city requiredstring
region requiredstringState, province or county
postal_code requiredstring
country requiredstringISO 3166-1 alpha-2

AddressInput

FieldTypeDescription
line1string
line2string
citystring
regionstringState, province or county
postal_codestring
countrystringISO 3166-1 alpha-2

AttributeCustomerRequest

FieldTypeDescription
code requiredstring
sourcelink | code | api
touch_timestring · date-timeWhen the visitor followed the link; default now

AttributeCustomerResponse

FieldTypeDescription
attributed requiredboolean
reason requiredstring

BlockCustomerRequest

FieldTypeDescription
reasonstringYour note on why; never shown to the customer.
categoryCustomerBlockCategoryWhy the customer is blocked. fraud and chargeback also refuse paying an invoice that is open.
blocked_untilstring · date-timeWhen the block ends by itself; must be in the future. Absent: until you lift it.
suspend_servicebooleanAlso suspend the customer's service: every entitlement check answers allowed false (suspended true) until the block is lifted or ends. Usage is still accepted and counted. Default false.

ContractAcceptance

FieldTypeDescription
id requiredstring · uuid
group_id requiredstring · uuidThe versions accepted together share it.
customer_id requiredstringEmpty for a guest payment.
version_id requiredstring · uuid
kind requiredContractKind
audience requiredContractAudienceA business customer gets the business series when one is published, else the consumer's.
version_number requiredinteger · int32
version_sha256 requiredstring
locale_requested requiredstring
locale_served requiredstring
body_sha256 requiredstringHex sha256 of the text served.
context_kind requiredhosted_invoice | api
context_ref requiredstring
source requiredhosted | api
observed requiredobjectip, user_agent, time, text: true when Payrudder observed it, false when the seller claimed it.
checkbox_text requiredstring
immediate_start_consent requiredboolean
immediate_start_text requiredstring
withdrawal_waiver_acknowledged requiredboolean
withdrawal_waiver_text requiredstring
variables requiredobject
snapshot_sha256 requiredstring
ip requiredstringBlank once the retention period passed.
user_agent requiredstring
accept_time requiredstring · date-time
customer_external_id requiredstring

ContractAcceptanceItem

FieldTypeDescription
version_id requiredstring · uuid
checkbox_text requiredstringThe words next to the box, as the customer saw them.

ContractAudience

consumer | business

ContractKind

distance_sales | pre_info | tos | privacy

ContractOrder

FieldTypeDescription
descriptionstring
totalstringFormatted for a person, e.g. 12.00.
currencystring

ContractRequirement

FieldTypeDescription
kind requiredContractKind
version_id requiredstring · uuid
version_number requiredinteger · int32
audience requiredContractAudienceA business customer gets the business series when one is published, else the consumer's.
locale requiredstringThe language of the text served.
body_html requiredstringThe text as safe HTML, variables resolved.
body_sha256 requiredstring
accepted requiredboolean
accept_time requiredstring · date-time
has_template_notice requiredboolean

ContractWithdrawal

FieldTypeDescription
id requiredstring · uuid
customer_id requiredstring · uuid
customer_external_id requiredstring
subscription_id requiredstring
invoice_id requiredstring
statement requiredstring
source requiredapi | console | hosted
status requiredreceived | accepted | rejected | refunded
receive_time requiredstring · date-time
decide_time requiredstring · date-time
decided_by requiredstring
decision_note requiredstring
credit_note_id requiredstringThe credit note billing made to refund the invoice, once one was requested (GET /v1/credit-notes/{id} says how the refund went).
refund_state requirednone | requesting | requested | refunded | refund_failed | outside | failednone: nothing asked of billing; requesting: being asked; requested: a credit note exists and the provider has not answered; refunded: the provider returned the money; refund_failed: the credit note exists and the provider did not return the money, retry the credit note's refund (a second credit note would credit the invoice twice), refund_note says why; outside: the money cannot go back through Payrudder (a bank transfer, cash), refund it by hand; failed: billing refused or did not answer, ask again with POST /v1/contract-withdrawals/{id}/refund. refunded and refund_failed are copied from the credit note's own result (credit_note.refunded, credit_note.refund_failed). The withdrawal's status stays the seller's to set.
refund_note requiredstring
refund_time requiredstring · date-time

CreateContractWithdrawalRequest

FieldTypeDescription
subscription_idstring · uuid
invoice_idstring · uuid
statementstring
receive_timestring · date-timeWhen the statement reached you; empty is now.
sourceapi | console

CreateCustomerRequest

FieldTypeDescription
external_id requiredstring
name requiredstring
email requiredstring · email
payment_methodstring
countrystringISO 3166-1 alpha-2
billing_currencystringISO 4217; default is the tenant's currency
addressAddressInputA postal address as printed on an invoice. Every part is optional.
localeen | tr | de | fr | es | arThe language of the customer's mails, receipts and payment pages
customer_typeindividual | businessA person or a taxpayer
phonestringThe customer's mobile number in international form, e.g. +90 532 123 45 67 or 0090 532 123 45 67; stored as E.164 (+905321234567). A number without the + or 00 prefix is refused: it cannot be read without a country.
referral_codestringThe code of the partner whose link or code brought this customer (phase 47). A code that cannot attribute does not fail the creation.
referral_touch_timestring · date-timeWhen the visitor followed that link; default now

CreatePaymentMethodSetupRequest

FieldTypeDescription
providerstripeThe provider whose page it is; only Stripe has one
return_url requiredstring · uriWhere the provider sends the customer after the page. `https` only

CreatePortalLinkRequest

FieldTypeDescription
expires_in_daysinteger

CreatePortalLinkResponse

FieldTypeDescription
token requiredstring
url requiredstring
expires_at requiredstring · date-time
customer_id requiredstring

CreditBalance

FieldTypeDescription
balance requiredstring · int64
next_expire_time requiredstring · date-timeWhen some of the credits run out; null when none does
lots requiredarray of CreditLot

CreditConsumeInput

FieldTypeDescription
credits requiredstring · int64int64, above zero (a number is accepted too)
reasonstring
idempotency_key requiredstringRequired, here or in the Idempotency-Key header

CreditGrantInput

FieldTypeDescription
credits requiredstring · int64int64, above zero (a number is accepted too)
source requiredpromo | manual
expire_timestring · date-timeMust lie in the future; leave out for credits that never expire
reasonstring
idempotency_keystring

CreditLedgerEntry

FieldTypeDescription
id requiredstring · uuid
lot_id requiredstring · uuid
kind requiredgrant | consume | expire | revoke
amount requiredstring · int64int64 as a string; negative for consume, expire and revoke (a revoke takes back the credits of a credit pack whose invoice was voided or credited; 0 when none of them was left to take, and its reason says how many could not be)
reason requiredstring
idempotency_key requiredstring
source requiredpurchase | promo | manualWhere the credits it touched came from; a purchase is a credit pack that was paid for
create_time requiredstring · date-time

CreditLedgerPage

FieldTypeDescription
entries requiredarray of CreditLedgerEntry
next_page_token requiredstring

CreditLot

FieldTypeDescription
id requiredstring · uuid
granted requiredstring · int64
remaining requiredstring · int64
source requiredpurchase | promo | manual
expire_time requiredstring · date-timeNull: the credits never expire
create_time requiredstring · date-time

CreditResult

FieldTypeDescription
balance requiredstring · int64int64 as a string: what the customer can spend after the call
replayed requiredbooleanTrue when the key had been used: nothing changed

Customer

FieldTypeDescription
id requiredstring · uuid
external_id requiredstring
name requiredstring
email requiredstring
payment_method requiredstring
create_time requiredstring · date-time
country requiredstringISO 3166-1 alpha-2
billing_currency requiredstringISO 4217
address requiredany
tax_id requiredstring
tax_office requiredstring
e_invoice_registered requiredboolean
locale requiredstringThe language of the customer's mails, receipts and payment pages: en, tr, de, fr, es or ar. Empty is the browser's language (pages) and English (mails).
customer_type requiredstringindividual (a person) or business (a taxpayer with a tax id). Empty is unknown.
block_timestring · date-timeSet while the customer is blocked: no new subscription, plan change or purchase (existing subscriptions run on). A block whose blocked_until has passed has ended, whatever this says.
block_reason requiredstringThe seller's own note on the block; never shown to the customer.
block_category requiredstringWhy: fraud, chargeback, abuse, request or other; empty when none was given.
blocked_untilstring · date-timeWhen the block ends by itself; absent: until it is lifted.
service_suspended requiredbooleanThe block also suspends the customer's service: every entitlement check answers allowed false. Read it with block_time and blocked_until, it holds only while the block does.
phone requiredstringThe customer's mobile number as E.164 (+905321234567); empty when none.
tax_id_validated_atstring · date-timeWhen tax_id last passed the local check (a TCKN or VKN checksum, the shape of a European VAT id). Not a registry lookup: no VIES, no GİB query. Absent when the id is empty or not recognised.
tax_id_warning requiredstringOne of tr_individual, tr_business, tr_any, tr_is_tckn, tr_is_vkn, eu_vat, or empty. A hint, never a refusal: the id does not look like the kind of number this customer should have (tr_individual: not a TCKN; tr_business: not a VKN; tr_any: neither; tr_is_tckn: a business was given a TCKN; tr_is_vkn: an individual was given a VKN; eu_vat: a business in the EU has no VAT id of its country's shape). Worked out from the stored id, country and customer_type, so it follows a change of any of them. Empty: nothing to say.

CustomerBlockAppeal

FieldTypeDescription
id requiredstring · uuid
status requiredopen | accepted | rejected | closed
statement requiredstringThe customer's own words. Untrusted: show as text only
answer requiredstringYour answer; empty until decided, and for an accepted appeal that had none
decided_by requiredstringA person's address, key:<api key id>, or system for a closed appeal
create_time requiredstring · date-time
decide_timestring · date-time

CustomerBlockCategory

fraud | chargeback | abuse | request | other

CustomerBlockEvent

FieldTypeDescription
id requiredstring · uuid
action requiredblocked | unblocked
category requiredstringEmpty when none was given; else a CustomerBlockCategory
reason requiredstring
blocked_untilstring · date-time
suspend_service requiredbooleanOn a block: whether it also suspended the customer's service
actor requiredstringA person's address, key:<api key id>, or operator
create_time requiredstring · date-time

CustomerCards

FieldTypeDescription
provider requiredstringThe provider that holds the cards; empty when the customer has none
cards requiredarray of StoredCard

CustomerContact

FieldTypeDescription
name requiredstring
email requiredstring · email
localeen | tr | de | fr | es | arThe language of the customer's mails, receipts and payment pages; omit to keep it
customer_typeindividual | businessA person or a taxpayer; omit to keep it
phonestringThe customer's mobile number in international form (+90 532 123 45 67 or 0090 532 123 45 67), stored as E.164. Omit to keep the stored number; send an empty string to clear it.

CustomerPayment

FieldTypeDescription
invoice_id requiredstring · uuid
invoice_number requiredstring
kind requiredcharge | refund
status requiredsucceeded | declined | failed | pending
amount_minor requiredstring · int64
currency requiredstring
provider requiredstring
environment requiredsandbox | live
occurred_at requiredstring · date-time

CustomerTax

FieldTypeDescription
tax_idstringVKN/TCKN (Turkey) or VAT id (EU)
tax_officestringVergi dairesi (Turkey)
e_invoice_registeredbooleanThe customer is registered for e-fatura

DecideCustomerBlockAppealRequest

FieldTypeDescription
decision requiredaccepted | rejected
answerstringRequired to reject

DeletedCustomerCard

FieldTypeDescription
provider requiredstring
deleted requiredboolean
was_in_use requiredbooleanThe deleted card was the one the customer's payment method names: set a new payment method

EntitlementOverride

FieldTypeDescription
customer_external_id requiredstring
meter_code requiredstring
create_time requiredstring · date-time
update_time requiredstring · date-time
limit requiredstringDecimal in meter units; "0" = unlimited
alert_thresholds_pct requiredarray of integerStrictly ascending percentages of the limit at which an alert fires; empty = 80 and 100
window requiredQuotaWindowWhat a limit counts over, in the tenant's time zone. Unspecified is the month (the billing period). A year is the calendar year (twelve billing periods); a lifetime limit never starts again.
mode requiredQuotaModeWhat reaching the limit means for `allowed` (Payrudder never refuses usage events). HARD (also unspecified): allowed turns false at the limit. SOFT: allowed stays true and the usage over the limit is billed at the meter's price, which the package must have. NOTIFY: allowed stays true; only the alerts are sent.

EntitlementStatus

FieldTypeDescription
customer_external_id requiredstring
meter_code requiredstring
period_id requiredstring
used requiredstringExact decimal with the meter's scale, examples: ["10.500"]
limit requiredstring,nullExact decimal; null when unlimited, examples: ["10.500"]
unlimited requiredboolean
allowed requiredboolean(unlimited, used < limit, or a soft or notify limit) and not suspended
as_of requiredstring,null · date-time
window requiredhour | day | week | month | year | lifetimeWhat the limit counts over; month for a meter without a limit
window_id requiredstringThe window used counts in: period_id for a month, 2026-11-05T13, 2026-11-05, 2026-W45, 2026 for a year or all for the lifetime
resets_at requiredstring,null · date-timeWhen the window ends and used starts again from zero; null for the lifetime window
mode requiredhard | soft | notify
over_limit requiredbooleannot unlimited and used >= limit, whatever the mode
overridden requiredbooleanThe limit is the customer's own override, not its package's
suspended requiredbooleanYou suspended this customer's service (a block with suspend_service): allowed is false on every meter, with or without a limit. Usage is still accepted and counted.

EntitlementTerms

FieldTypeDescription
limit requiredstringDecimal in meter units; "0" = unlimited
alert_thresholds_pctarray of integerStrictly ascending percentages of the limit at which an alert fires; empty = 80 and 100
windowQuotaWindowWhat a limit counts over, in the tenant's time zone. Unspecified is the month (the billing period). A year is the calendar year (twelve billing periods); a lifetime limit never starts again.
modeQuotaModeWhat reaching the limit means for `allowed` (Payrudder never refuses usage events). HARD (also unspecified): allowed turns false at the limit. SOFT: allowed stays true and the usage over the limit is billed at the meter's price, which the package must have. NOTIFY: allowed stays true; only the alerts are sent.

Error

FieldTypeDescription
error requiredobject

GetContractRequirementsResponse

FieldTypeDescription
enforced requiredbooleanThe seller requires agreements at all.
requirements requiredarray of ContractRequirement
satisfied requiredboolean
immediate_start requiredbooleanA consumer is asked for the request to start at once and the waiver acknowledgement.
missing_kinds requiredarray of ContractKindRequired kinds with no published version: a payment is refused.
audience requiredContractAudienceA business customer gets the business series when one is published, else the consumer's.

GetCustomerUsageResponse

FieldTypeDescription
customer_external_id requiredstring
period_id requiredstring
usage requiredarray of GetCustomerUsageUsage

GetCustomerUsageUsage

FieldTypeDescription
meter_code requiredstring
quantity requiredstring
event_count requiredstring · int64

GrantCustomerCouponRequest

FieldTypeDescription
coupon_code requiredstring
source requiredstringWho gives it, e.g. operator or affiliate
source_ref requiredstring

GrantCustomerCouponResponse

FieldTypeDescription
granted requiredboolean

Invoice

FieldTypeDescription
id requiredstring
customer_id requiredstring
customer_external_id requiredstring
period_id requiredstring
kind requiredREGULAR | CORRECTION | FEE | LINKFEE: a fixed fee invoiced in advance for one period, or what a plan change moved. LINK: made from an ad-hoc payment link
number requiredstring
currency requiredstring
subtotal_minor requiredstring · int64
total_minor requiredstring · int64
status requireddraft | open | paid | uncollectible | void
saga_state requiredstring
create_time requiredstring · date-time
finalize_time requiredstring · date-time
lines requiredarray of InvoiceLine
payments requiredarray of InvoicePaymentPayments recorded by hand by the operator (they do not come through a payment provider), oldest first. Only the single-invoice reads fill it.
plan_version_id requiredstring
pdf_object_key requiredstring
credit_surplus_minor requiredstring · int64What the lines came to below zero (late-usage corrections larger than everything else on the invoice). The total is never negative, so this was added to the customer's credit balance instead, once per invoice, and their next invoices use it. 0 for every other invoice

InvoiceLine

FieldTypeDescription
line_no requiredinteger
kind requiredBASE_FEE | USAGE | LATE_USAGE | CREDIT | CORRECTION | DISCOUNT | TAX
meter_code requiredstring
origin_period_id requiredstring
quantity requiredstring
amount_minor requiredstring · int64
description requiredstring
service_from requiredstring · date-timeThe service period the line covers, nullable: true
service_to requiredstring · date-time

InvoicePayment

FieldTypeDescription
id requiredstring
invoice_id requiredstring
method requiredbank_transfer | cash | other
amount_minor requiredstring · int64
currency requiredstring
received_at requiredstring · date-time
reference requiredstring
note requiredstring
recorded_by requiredstring
recorded_at requiredstring · date-time

ListContractAcceptancesResponse

FieldTypeDescription
acceptances requiredarray of ContractAcceptance
next_page_token requiredstringPass it as page_token for the next page; empty on the last page

ListCustomerBlockAppealsResponse

FieldTypeDescription
appeals requiredarray of CustomerBlockAppeal

ListCustomerBlockEventsResponse

FieldTypeDescription
events requiredarray of CustomerBlockEvent

ListCustomerPaymentsResponse

FieldTypeDescription
invoices requiredarray of Invoice
payments requiredarray of CustomerPayment

ListCustomersResponse

FieldTypeDescription
customers requiredarray of Customer
next_page_token requiredstring

ListEntitlementOverridesResponse

FieldTypeDescription
overrides requiredarray of EntitlementOverride

ListEntitlementsResponse

FieldTypeDescription
items requiredarray of EntitlementStatus

PaymentMethodSetup

FieldTypeDescription
url requiredstringThe provider's page: send it to the customer
ref requiredstringThe provider's reference of the page (Stripe's Checkout Session id)

QuotaMode

QUOTA_MODE_UNSPECIFIED | QUOTA_MODE_HARD | QUOTA_MODE_SOFT | QUOTA_MODE_NOTIFY

QuotaWindow

QUOTA_WINDOW_UNSPECIFIED | QUOTA_WINDOW_MONTH | QUOTA_WINDOW_HOUR | QUOTA_WINDOW_DAY | QUOTA_WINDOW_WEEK | QUOTA_WINDOW_YEAR | QUOTA_WINDOW_LIFETIME

RecordContractAcceptanceRequest

FieldTypeDescription
localestringThe language the page was shown in.
context_refstringYour reference for the checkout (an order id).
items requiredarray of ContractAcceptanceItem
immediate_start_consentboolean
immediate_start_textstring
withdrawal_waiver_acknowledgedboolean
withdrawal_waiver_textstring
ipstringYour claim about the customer's address.
user_agentstringYour claim about the customer's browser.
accept_timestring · date-timeWhen the customer accepted; empty is now.
customer_typeindividual | business
orderContractOrder

RecordContractAcceptanceResponse

FieldTypeDescription
group_id requiredstring · uuid
acceptances requiredarray of ContractAcceptance

RevokePortalLinksResponse

FieldTypeDescription
revoked requiredinteger

SetPaymentMethodRequest

FieldTypeDescription
payment_method requiredstring

StoredCard

FieldTypeDescription
token requiredstringThe provider's card token (cardToken): what the delete names
alias requiredstring
bin requiredstringThe first digits of the number (the BIN)
last4 requiredstring
type requiredstringCREDIT_CARD, DEBIT_CARD or PREPAID_CARD
association requiredstringVISA, MASTER_CARD, ...
family requiredstring
bank requiredstring
expire_month requiredstring
expire_year requiredstring
in_use requiredbooleanThe customer's payment method names this card

UpcomingInvoice

FieldTypeDescription
estimate requiredbooleanAlways true. The preview is never the invoice
customer_external_id requiredstring
period_id requiredstringThe period whose invoice this is, in the tenant's time zone
period_start requiredstring · date-time
period_end requiredstring · date-time
currency requiredstring
lines requiredarray of InvoiceLineThe lines as the invoice would have them, in order: fixed fee, usage, late usage, tax, and the credit
subtotal_minor requiredstring · int64Before tax and after the credit, as on an invoice
tax_minor requiredstring · int64
credit_minor requiredstring · int64The credit balance the invoice would use, a positive number
total_minor requiredstring · int64What would be charged; never negative
credit_surplus_minor requiredstring · int64What the lines come to below zero, which the real invoice would add to the customer's credit balance instead of charging a negative total; 0 normally
as_of requiredstring · date-timeThe clock of the estimate
has_invoice requiredbooleanFalse when the customer has no subscription in the period and no late usage to bill