Developer documentation
Invco Payments API
Take card payments from your own website or app against a SumUp merchant account connected to Invco. One POST opens a hosted checkout and returns a payment link and a QR code; a webhook and a status endpoint tell you when it is paid.
Overview
Invco is an integration platform, not a payment provider. Every business connects its own SumUp merchant account, and money moves from the payer to that merchant account directly. Invco never holds, routes, or settles funds, and never sees a card number — all card entry happens on SumUp's own hosted checkout.
Concretely, that means the base URL is Invco but the money is not. You call Invco; Invco calls SumUp on the merchant’s behalf; the payer completes the payment on a SumUp-hosted page; SumUp settles to the merchant’s bank. Invco records the reference and the status, and nothing else about the card.
Base URL https://invco.pro
Auth Bearer token (payments API key)
Format JSON request and response bodiesAn API key belongs to one company inside Invco, and every payment it opens is scoped to that company’s connected SumUp account(s). If you run several companies, issue a key per company rather than sharing one.
Authentication
Create a key in Invco under Payments → API keys. The key is shown once, at creation. Send it on every request as a bearer token, or in x-api-key if that suits your stack better:
Authorization: Bearer inv_live_xxxxxxxxxxxxxxxxxxxx
# or
x-api-key: inv_live_xxxxxxxxxxxxxxxxxxxxThis is not the same key as the MCP key used by AI assistants. They are separate credentials with separate scopes; a payments key cannot read your invoices or mail, and an MCP key cannot open a checkout.
A key may optionally be restricted to a list of allowed origins. When set, a request carrying a browser Origin or Referer from any other host is rejected. Server-to-server calls send neither header and are unaffected — which is the intended way to use the key, since a secret should never be shipped to a browser in the first place.
Create a checkout
POST /api/checkout creates an invoice, opens a SumUp hosted checkout against one of the company’s connected accounts, emails the invoice to the customer, and returns the pay link and a QR code.
curl -X POST https://invco.pro/api/checkout \
-H "Authorization: Bearer $INVCO_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-10482" \
-d '{
"currency": "GBP",
"customer": {
"name": "Ada Lovelace",
"email": "ada@example.com",
"companyName": "Northgate Studios Ltd"
},
"items": [
{ "description": "Website retainer — August", "quantity": 1, "unitPrice": 2000, "taxRate": 20 }
],
"reference": "order-10482",
"return_url": "https://yoursite.com/thanks",
"notes": "Thanks for your business."
}'Request body
| Field | Type | Notes |
|---|---|---|
| customer | object (required) | name and email are required; phone, address and companyName are optional. A client record is found-or-created by email, so repeat customers don’t duplicate. |
| items | array | Line items: description, unitPrice, and optional quantity, taxRate, details. Max 100. Provide this or amount. |
| amount | number | A single total, when you don’t need itemisation. One of amount or items must be present. |
| currency | string | ISO 4217, defaults to USD. See currencies. |
| reference | string | Your own order reference, stored on the payment and shown in statements. |
| return_url | string (url) | Where the payer is sent after paying. See return URL. |
| due_date | string | Any parseable date. Empty string means no due date. |
| notes | string | Free text placed on the invoice. |
Response
{
"checkout_id": "cmt7f0a2b0001lq08xyz12345",
"invoice_id": "cmt7f0a2b0002lq08abcdef01",
"invoice_number": "INV-2041",
"amount": 2400,
"currency": "GBP",
"status": "PENDING",
"payment_url": "https://pay.sumup.com/b2c/XXXXXXXX",
"qr_code": "data:image/png;base64,iVBORw0KGgo..."
}Redirect the payer to payment_url, or render qr_code (a data URI, safe to drop straight into an <img>) if they are paying from a different device.
Idempotency
Send an Idempotency-Key header — your order id is a good choice. A repeat request with the same key returns the original checkout instead of creating a second invoice and charging the customer twice.
Keys are scoped to the API key that created them and are retained for the lifetime of the payment record, so replaying a key months later still returns the original checkout rather than opening a new one. Use a fresh key when you genuinely intend a new payment — including when a customer retries after a failure, since a failed or expired checkout cannot be reopened.
Check a payment
GET /api/checkout/{checkout_id} returns the current state. If the payment is still pending, Invco re-checks it live with SumUp before answering, so this endpoint is authoritative — it is the right way to confirm a payment before releasing goods.
curl https://invco.pro/api/checkout/cmt7f0a2b0001lq08xyz12345 \
-H "Authorization: Bearer $INVCO_KEY"
{
"checkout_id": "cmt7f0a2b0001lq08xyz12345",
"status": "PAID",
"amount": 2400,
"currency": "GBP",
"invoice_number": "INV-2041",
"payment_url": null
}Webhooks
Set a webhook URL on the API key in Invco. When a payment succeeds, Invco POSTs JSON to it:
{
"event": "payment.paid",
"checkout_id": "cmt7f0a2b0001lq08xyz12345",
"provider_ref": "TE7X9K2M",
"invoice_id": "cmt7f0a2b0002lq08abcdef01",
"invoice_number": "INV-2041",
"amount": 2400,
"currency": "GBP",
"method": "card",
"paid_at": "2026-08-25T11:04:22.518Z"
}payment.paid is currently the only event emitted. Failures and expiries are recorded on the payment but are not pushed to you — poll the status endpoint if you need to react to them.
The webhook is not signed today. There is no HMAC header to verify, so treat the notification as a hint rather than proof: when it arrives, call GET /api/checkout/{id} with your API key and act on that answer. Keep your endpoint URL unguessable in the meantime. Signed webhooks are on the roadmap; this note will be removed when they ship, not before.
Delivery is a single attempt with an 8-second timeout, and there is no automatic retry queue — another reason to treat the status endpoint as the source of truth. Return a 2xx quickly and do your own work asynchronously. Your endpoint must be a public HTTPS address; Invco re-resolves it at send time and refuses to post to private or internal addresses.
Return URL
After paying, the customer is sent to a return URL. It is resolved in this order: the return_url in the checkout request, then the default configured on the API key, then Invco’s own hosted receipt page.
The return URL is a navigation, not a notification — the customer can close the tab before it loads, and it carries no proof of payment. Use it for the “thanks, here’s your order” screen, and confirm the payment server-side via the status endpoint.
Payment lifecycle
PENDING ──► PAID payment cleared; invoice marked paid; webhook sent
│
├─────► FAILED the payer's attempt was declined
└─────► EXPIRED the checkout timed out unusedA checkout is single-use. If a payer fails or lets one expire, create a new checkout for the retry rather than resending the old link.
Invco also runs a reconciliation pass over outstanding payments, re-checking them against SumUp. A notification lost in transit therefore does not leave a paid invoice showing as unpaid — it is corrected on the next pass.
Rate limits
| Field | Type | Notes |
|---|---|---|
| per IP | 120 / min | Applied before authentication, so invalid keys can’t be sprayed cheaply. |
| per API key | 60 / min | Each call mints a real invoice and opens a real checkout, so this limit protects the merchant account as much as the server. |
Exceeding either returns 429 with a Retry-After header. Back off and retry with the same idempotency key.
Errors
Errors are JSON with an error message and a conventional status code.
| Field | Type | Notes |
|---|---|---|
| 400 | Bad request | Validation failed — the message names the specific problem, e.g. “Unsupported currency” or “Provide either amount or items”. |
| 401 | Unauthorized | Missing, unknown, revoked, or origin-rejected API key. |
| 404 | Not found | No payment with that id under this API key’s company. |
| 429 | Too many requests | Rate limited; see Retry-After. |
| 502 | Provider error | SumUp rejected or could not fulfil the checkout — for example no connected account could open one. Safe to retry with the same idempotency key. |
Currencies
Requests are validated against the settlement currencies SumUp supports:
EUR GBP USD CHF SEK NOK DKK PLN HUF CZK BGN RON HRK
BRL AUD CAD AED INR PKR NZD ZAR MXN CLP COP SGD HKD JPYPassing the check here does not guarantee SumUp will accept it — what a given merchant account can actually settle depends on that account’s own configuration and country. Test one live transaction in each currency you intend to use.
Disconnecting & deletion
Revoke an API key in Invco and it stops working immediately; any site still using it receives 401 on the next call. Removing a connected SumUp account deletes the stored credential, and Invco can no longer open checkouts against it. Revoking the key from the SumUp dashboard instead has the same effect and is instant.
Payments already taken remain in the merchant’s records, and in SumUp’s — they are the merchant’s accounting history, not ours to remove. Deletion requests for personal data are handled as described in the Privacy Policy.
Not implemented yet
Listed so you can plan around them rather than discover them:
- Signed webhooks (HMAC) — verify via the status endpoint for now.
- Webhook retries — a failed delivery is not re-queued.
- Events other than
payment.paid. - Refunds through the API — issue them from the SumUp dashboard.
- Saved cards / recurring charges — dependent on SumUp enabling tokenisation on the merchant account.
- A sandbox mode on Invco’s side — test with a SumUp sandbox merchant and a small live amount.
SumUp is a registered trademark of SumUp Limited. Invco is an independent integration and is not affiliated with, endorsed by, or sponsored by SumUp.