Browse the documentation

Invoices, balance and payments

GET /invoices

Permissions: billing.invoices:read

ParameterTypeRequiredValues
limitintegerno, default 201 to 100
cursorstringnopagination.nextCursor from the previous page
statusstringnoUNPAID, PARTIALLY_PAID, PAID, OVERDUE, VOID, PARTIALLY_REFUNDED, REFUNDED

DRAFT is accepted but never returns anything: drafts do not leave the dashboard.

curl -H "Authorization: Bearer $FRESHPERF_KEY" \
     "https://api.freshperf.fr/v1/invoices?status=UNPAID&limit=20"
{
  "data": [
    { "code": "INV-202608-0082", "kind": "RENEWAL", "status": "PAID",
      "issuedAt": 1787727600380, "dueAt": 1788300000000, "paidAt": 1787727605569,
      "currency": "EUR", "totalCents": 360, "amountPaidCents": 360,
      "balanceDueCents": 0, "serviceCode": "SRV-TISZG5-6000",
      "orderNumber": "ORD-20260727-0001" }
  ],
  "pagination": { "nextCursor": "MTg4", "limit": 20 }
}
FieldTypeNotes
codestringthe identifier every invoice route takes
kindstring or nullRENEWAL, UPGRADE, ONE_OFF, ADDON_ADDED, ADDON_REMOVED, ADMIN_RESIZE
statusstringthe filter values above
issuedAt, dueAtintegerepoch milliseconds
paidAtinteger or nullepoch milliseconds
totalCents, amountPaidCents, balanceDueCentsintegercents, tax included
serviceCodestring or nullthe service billed, when the invoice bills one
orderNumberstring or nullthe originating order

GET /invoices/{code}

Permissions: billing.invoices:read

Carries the fields above, adds subtotalCents, discountCents, amountRefundedCents, and details:

lines[] fieldTypeNotes
namestring
descriptionstring or null
quantityinteger
unitPriceCents, unitTaxCents, totalCentsintegercents
periodStart, periodEndinteger or nullepoch milliseconds
recurrencestring or null
payments[] fieldTypeNotes
idinteger
statusstringPENDING, AUTHORIZED, CAPTURED, SETTLED, FAILED, CANCELLED, REFUNDED, NEEDS_REVIEW
amountCentsintegercents
currencystring or null
providerstring or nullthe payment provider, or BALANCE for a balance payment
methodstring or nulle.g. CARD
createdAtintegerepoch milliseconds

GET /invoices/{code}/pdf

Permissions: billing.invoices:read

Answers the file, not JSON.

HeaderValue
Content-Typeapplication/pdf
Content-Dispositionattachment; filename="INV-202608-0082.pdf"
Cache-Controlprivate, no-store
curl -H "Authorization: Bearer $FRESHPERF_KEY" \
     -o invoice.pdf \
     https://api.freshperf.fr/v1/invoices/INV-202608-0082/pdf
ErrorStatusWhen
INVOICE_NOT_FOUND404unknown code, or another account's invoice
PDF_NOT_AVAILABLE503the document is not rendered yet. Retry-After: 5

On an error the body goes back to the usual JSON envelope.

POST /invoices/{code}/pay

Permissions: billing.invoices:pay, plus billing.saved_methods:charge with saved_method. Required header: Idempotency-Key.

Pays what is left due.

FieldTypeRequiredValuesWhere it comes from
methodstringyesbalance, saved_method-
paymentMethodIdintegeryes when saved_method> 0GET /payment-methodsid

none is not accepted here: 400 VALIDATION_ERROR:method.

curl -X POST -H "Authorization: Bearer $FRESHPERF_KEY" \
     -H "Content-Type: application/json" \
     -H "Idempotency-Key: pay-INV-202608-0082" \
     -d '{"method":"balance"}' \
     https://api.freshperf.fr/v1/invoices/INV-202608-0082/pay
{ "data": { "status": "CAPTURED", "paymentId": 244, "amountCents": 360,
            "invoiceCode": "INV-202608-0082", "balanceCents": 4640 } }
FieldTypeNotes
statusstringNONE, FREE, or a Payment.Status value
paymentIdinteger or null
amountCentsinteger or nullcents charged
invoiceCodestring or nullthe document settled
balanceCentsinteger or nullthe balance afterwards, on a balance payment only
ErrorStatusWhen
INVOICE_NOT_FOUND404unknown code
INVOICE_ALREADY_PAID400already settled
INVOICE_VOID, INVOICE_REFUNDED, INVOICE_NOT_ISSUED, INVOICE_BALANCE_ZERO400nothing to pay on this document
CURRENCY_UNSUPPORTED400currency not supported
INSUFFICIENT_BALANCE402balance too short
PAYMENT_DENIED402the bank refused
PAYMENT_METHOD_NOT_FOUND404method not on the account
PAYMENT_METHOD_NOT_ACTIVE400method expired or blocked
API_KEY_SPENDING_CAP_EXCEEDED403the key's cap does not cover the amount
PAYMENT_IN_PROGRESS409another payment of the same document is running
PAYMENT_RECORDED_MISMATCH409reconciliation anomaly. Contact support

GET /credit-notes

Permissions: billing.credit_notes:read

ParameterTypeRequiredValues
limitintegerno, default 201 to 100
cursorstringnooffset cursor
FieldTypeNotes
codestringAV-202608-0001
statusstringISSUED or VOID
invoiceCodestring or nullthe invoice it corrects
currencystring or null
totalCentsintegercents
reasonstring or null
issuedAtintegerepoch milliseconds

GET /credit-notes/{code}/pdf

Permissions: billing.credit_notes:read

Same headers and same errors as the invoice PDF, with CREDIT_NOTE_NOT_FOUND on 404.

GET /balance

Permissions: billing.balance:read

{ "data": { "enabled": true, "balanceCents": 4400,
            "currency": "EUR", "autoRenewFromBalance": false } }
FieldTypeNotes
enabledbooleanfalse when the prepaid balance is not open on the platform
balanceCentsintegercents available
currencystring or nullISO 4217
autoRenewFromBalancebooleanthe account opted to pay its renewals from the balance

The balance is topped up from the dashboard. The API spends it; it does not fill it.

GET /balance/transactions

Permissions: billing.balance:read

ParameterTypeRequiredValues
limitintegerno, default 201 to 100
cursorstringnooffset cursor
{ "data": [
  { "id": 62, "type": "RENEWAL_APPLIED", "amountCents": -300,
    "balanceAfterCents": 4400, "currency": "EUR",
    "reason": "Invoice INV-202608-0087", "relatedPaymentId": null,
    "createdAt": 1787907506000 }
] }
FieldTypeNotes
idinteger
typestring or nullsee the table below
amountCentsintegersigned: positive on a credit, negative on a debit
balanceAfterCentsintegerthe balance just after this movement
reasonstring or nullfree-form label
relatedPaymentIdinteger or nullpoints at an invoice's payments[].id
createdAtintegerepoch milliseconds
typeMeaning
TOPUP, TOPUP_CARD, TOPUP_PAYPAL, TOPUP_SAVEDa top-up, by the method used
PAYMENT_APPLIEDthe balance paid an order or an invoice
RENEWAL_APPLIEDthe balance paid a renewal
PAYMENT_REVERSEDa balance payment was undone
REFUND_TO_BALANCEa refund came back to the balance
REFERRAL_CREDITreferral earnings
ADMIN_CREDIT, ADMIN_DEBITa gesture or a correction

GET /payment-methods

Permissions: billing.payment_methods:read

The whole list, unpaginated.

{ "data": [
  { "id": 14, "type": "CARD", "status": "ACTIVE",
    "displayName": "MASTERCARD ••••0011", "brand": "MASTERCARD",
    "last4": "0011", "expMonth": 9, "expYear": 2028, "isDefault": true }
] }
FieldTypeNotes
idintegerpass it back as paymentMethodId
typestringCARD, SEPA_DIRECT_DEBIT, BANK_TRANSFER, PAYPAL, APPLE_PAY, GOOGLE_PAY, OTHER
statusstringACTIVE, INACTIVE, PENDING_VERIFICATION, EXPIRED, BLOCKED
displayNamestring or nullmasked label. A PayPal address is partly hidden
brand, last4string or nullcards
expMonth, expYearinteger or nullcards
isDefaultbooleanthe method account_default uses

Only an ACTIVE method can be charged. Adding or removing one happens in the dashboard.

What the key has spent

GET /me and GET /me/key return key.spendingCapCents and key.spendingRemainingCents: the key's cap over a rolling 30 days and what is left of it. The individual charges are in the key's log, in the dashboard.