Skip to content
Coritan Docs

Handle billing in the staff console

Work with invoices, transactions, refunds, disputes, dunning, coupons, orders and services from the staff console.

View as Markdown

The billing pages of the staff console hold the money side of your storefront. /staff/invoices lists what your customers owe and have paid, /staff/payments shows every payment with the charges that failed and the chargebacks, /staff/refunds holds the refunds customers ask for, /staff/orders lists every order, and /staff/coupons the codes your checkout accepts. From an invoice or an order, your team takes payment, gives refunds, and changes or ends the customer's service.

Any member can read invoices and orders. On these pages each role can do everything the roles below it can, and the other tasks need:

Task Lowest role Step-up
Download an invoice, and read refund requests, coupons and service notes Tier 3 support No
Suspend a service or lift its suspension, cancel it, retry a stuck order, add a service note Tier 3 support No
Raise, edit, cancel or resend an invoice Billing No
Charge a saved payment method, mark an invoice paid, refund an invoice Billing Yes
Approve or reject a refund request Billing Yes
Read payments, failed charges and disputes, and export payments or invoices Billing No
Change a service's plan or its next due date Billing No
Turn coupons on or off, and create or change a coupon Billing No
Terminate a service Admin Yes

A step-up is a fresh password or authenticator code, which lasts 10 minutes; Sign in to the staff console explains it.

  1. Open /staff/invoices.
  2. Search by invoice number or ID, or by the customer's email, name, username or company.
  3. Narrow the list to one status, or to the invoices that are due: unpaid and overdue together. You can also keep only the invoices of paying customers, or only those of free ones.

Invoices for paid services come first, then the newest. An invoice's page shows its lines with the service each one pays for, the customer and their credit balance, up to 20 payments and refunds, and any plan change the invoice pays for. While the invoice is open, the page also shows the automatic charge schedule behind it and whether the customer has a saved payment method. Manage customer invoices says what each status means.

Raise a one-line invoice for a setup fee, an add-on or a correction:

  1. On /staff/invoices, create an invoice and choose the customer.
  2. Enter a description of up to 500 characters, the amount and any tax. The invoice is in the customer's currency unless you choose another.
  3. Choose how many days the customer has to pay, 0–365 (7 unless you change it), and add notes of up to 2,000 characters if you need them.
  4. To list it with an order's invoices, choose the order.

The invoice starts unpaid, and we email it to the customer unless you turn that off. An invoice for nothing is never emailed.

While an invoice is open, we charge the customer's saved payment method on a schedule until it is paid, and the invoice's page shows the next attempt and the last error. To act sooner, open the invoice and choose one of these:

  • Charge it now. We charge the saved payment method at once, as the next automatic attempt would. Nothing is charged when the customer turned automatic payment off or a payment is still settling. When the bank wants the customer to confirm the payment, ask them to pay the invoice from their account.
  • Resend it. We email the invoice again, or the overdue notice once it is past its due date. Nothing about the invoice changes.
  • Mark it paid, when the customer paid you another way, such as by bank transfer. The invoice becomes paid for its full amount, and its services renew as they would after any payment. Coritan did not receive this money, so it is not part of a payout.

Each member may charge each invoice 30 times an hour, and mark 60 invoices paid an hour.

  • Edit the notes, or move the due date of an open invoice. An overdue invoice whose due date moves into the future is unpaid again.
  • Cancel an invoice nobody should pay. Nothing is owed on it afterwards, and an upgrade it was paying for is called off. A paid invoice is refunded instead.

Refund a paid invoice when your team decides to: a goodwill gesture, a duplicate charge or a mistake.

  1. Open the invoice and choose to refund it.
  2. Enter the amount, up to what the customer paid, and a reason of 5–500 characters.
  3. Choose where the money goes: back to the payment method, or into the customer's wallet as credit.
  4. Confirm it is you if the console asks.

Your team can refund a service that ended for breaking your terms, which a customer cannot ask for. No refund covers a cryptocurrency payment, an invoice that is not paid, or one already refunded. A payment made from the customer's wallet, or an invoice your team marked paid, can only go back as credit. A refund of the whole invoice ends the service it paid for at once.

We email the customer when the refund goes through. Each member may make 30 refunds an hour.

Customers ask for refunds from their account, and each request waits on /staff/refunds, oldest first. Tier 3 support can read them; a billing member, an admin or the owner decides.

  1. Open /staff/refunds and choose a request. It shows the invoice, the service, the customer's reason, and our check against your refund policy: whether the request came within 24 hours of paying for a new service, or 48 hours for a renewal.
  2. Approve it, choosing to send the money back to the payment method or into the wallet as credit, or reject it with a reason of 5–2,000 characters.
  3. Confirm it is you if the console asks.

We email the customer your decision, with your reason when you reject it. When the payment gateway declines an approved refund, the request becomes failed with the gateway's words, and nothing is refunded.

Follow failed payments and disputes

Section titled Follow failed payments and disputes

/staff/payments is for billing members, admins and the owner:

  • Payments lists every payment, refund, chargeback and change to a wallet, newest first. Search by the gateway's reference, the description, or a payment or invoice ID, and filter by kind, gateway, customer or dates. The totals above it cover the last 30 days, and add amounts as they are, without converting between currencies.
  • Failed charges lists the open invoices our automatic charges are working on, the next attempt first, with the attempts made, the next one and the last error. A schedule that ran out of attempts or time shows gave_up: charge the invoice yourself when the customer has fixed their card, or ask them to pay.
  • Disputes lists the chargebacks customers opened with their bank, with the amount, the reason, where the evidence stands and when it is due. Coritan prepares and submits the evidence, as Follow payment disputes explains.

A billing member can download the payments and the invoices as CSV, up to 20,000 rows each. Exports count towards a limit of 12 an hour for each member.

/staff/orders lists an order for each service a customer bought. Search by hostname, order ID, or the customer's email, name, username or company, and filter by status. An order's page shows the customer with their credit balance, up to 20 invoices, and the server behind the order once it has one.

An order is stuck when it is pending, provisioning or failed while it is paid for, or was never invoiced; the stuck filter lists these. An order waiting for its invoice to be paid is not stuck: we build it when the invoice is paid.

To build a stuck order, retry it from its page. It must not have a server yet or a build already queued. Each member may retry an order 8 times in 10 minutes.

Open the order to act on the service behind it:

  • Suspend it, with a reason of up to 300 characters. The customer sees it on the service and in the email we send, so say what they need to fix. Lifting the suspension brings the service back, emails the customer, and calls off a deletion your team scheduled with the suspension.
  • Cancel it for the customer, now or at the end of the term, as their own cancel button would. The preview shows the credit for unused time the customer gets, if any, and we take a snapshot of a server first unless you say not to. When your storefront sells a free plan, cancelling a server moves it onto that plan instead of ending it.
  • As an admin, terminate it for abuse, for fraud, or after a refund. The service ends now with no credit for unused time, we keep a snapshot, and your reason of 3–500 characters stays on the record. We email the customer only when you choose to.
  • Move it to another plan with the same billing cycle. An upgrade raises an invoice for the difference over the rest of the term, and the plan changes when the customer pays it. A downgrade may credit their wallet with the difference. You can call off an upgrade that is waiting for payment.
  • Move its next due date, with a reason of 3–500 characters: later as a goodwill gesture, or sooner to correct a mistake. The date cannot be in the past, and a service that has ended keeps its date.
  • Add a note of up to 8,000 characters for your team. The order shows the newest 100.

Coupons are off until a billing member turns them on at /staff/coupons. While they are off, the checkout shows no code field and refuses every code.

  1. Create a coupon with a code of 2–40 letters, digits, - and _. We store it in capitals, and customers can type it in either case.
  2. Choose a percentage of up to 100, or a fixed amount in one currency.
  3. Limit it if you need to: to some products, to a number of uses in all, to a number of uses for each customer (1 unless you change it, or 0 for no limit), and to the dates it starts and ends.

A coupon comes off the first invoice of an order, before tax, and the order renews at its plan's price. A fixed amount takes nothing off an order in another currency. A coupon expires once it reaches its number of uses. Pause a coupon to stop it for a while, and open it to see each use with the customer, the invoice and the amount it took off.

Each change shows on the invoice, order or coupon at once. The customer gets the emails described above, and the audit log records who did what.

Invoice is paid; only an unpaid or overdue invoice can be charged
Only an open invoice can be charged or resent. A paid invoice needs nothing more.
No saved payment method to charge; the customer pays by hand or adds one.
The customer has no saved payment method. Ask them to pay the invoice from their account.
Customer authentication required. Ask the customer to complete the payment.
The bank wants the customer to confirm this payment. Ask them to pay the invoice from their account.
A paid invoice is refunded, not cancelled
Refund the invoice instead.
This invoice carries a pending plan change in its notes; they cannot be edited until it is paid or cancelled.
The invoice pays for an upgrade, and its notes hold that change. Wait until the customer pays it, or cancel the invoice to call off the upgrade.
Only an open invoice can have its due date moved
Only a draft, unpaid or overdue invoice has a due date to move.
Only 20.0000 has been paid on this invoice
You tried to refund more than the customer paid. Refund at most the amount it names.
A refund request is already pending on this invoice; decide that one first
The customer has asked for a refund of this invoice. Decide their request on /staff/refunds.
This payment cannot be sent back to its payment method; approve it as wallet credit instead
The customer paid from their wallet, your team marked the invoice paid, or the gateway cannot refund. Refund it as wallet credit.
Cryptocurrency payments are not refundable in any form; see the Refund Policy.
We never refund a cryptocurrency payment, as money or as credit.
Give the customer a reason; it goes in the email
A rejection needs a reason of at least 5 characters.
This request has already been decided
Someone on your team approved or rejected the request first, or the customer withdrew it.
Plan changes must keep the current billing cycle
Choose a plan with the same billing cycle. The plan options list only those.
The due date cannot be in the past
Choose today or a later day.
Service already cancelled
The service is terminated, or waiting to be.
This order already has a server; open it instead
The order has been built. Open its server from the order's page.
That code already exists
Codes are unique within your organization, whatever their case. Choose another.

These routes live under https://api.coritan.com/api/v1/orgs/{org_slug}/staff/, and take a console session or a member's access token as The staff console explains. The reference lists them in five groups:

Area Reference
Invoices Invoices in the staff console
Payments, failed charges and disputes Staff billing
Orders and refund requests Staff operations
Services Services in the staff console
Coupons Staff coupons

A missing invoice answers 404 Invoice not found, and a missing order or service 404 Order not found.

Shell
curl "https://api.coritan.com/api/v1/orgs/acme/staff/invoices?status_filter=due&audience=paid&limit=50" \
  -H "Authorization: Bearer $STAFF_TOKEN"

q takes up to 200 characters, customer_id one customer, limit 1–200 (50 by default) and offset. status_filter is a status, or due for unpaid and overdue together. audience is paid, free or all: an invoice is paid when a line bills a service outside your free tier, and an invoice with no service follows its customer. The answer is a list of invoices, each with invoice_number, status, subtotal, tax, total, amount_paid, amount_remaining, currency, due_date, paid_at, customer and is_free.

GET /staff/invoices/stats answers all, due and one count per status, under the same audience. GET /staff/invoices/{invoice_id} answers the invoice, its items, up to 20 transactions, the customer, any plan_change, the charge_schedule and payment_method_available. GET /staff/invoices/{invoice_id}/pdf downloads the PDF the customer sees.

Shell
curl -X POST https://api.coritan.com/api/v1/orgs/acme/staff/invoices \
  -H "Authorization: Bearer $STAFF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"customer_id": 812, "description": "Setup of a custom modpack", "amount": "25.00", "due_days": 14}'

Send customer_id, a description of 1–500 characters and an amount above zero. tax defaults to 0, currency to the customer's, due_days to 7 (0–365), and send_email to true. notes takes up to 2,000 characters, and service_id ties the line to one of the customer's orders. It answers 201 with the id, invoice_number, status, total, currency and due_date.

PATCH /staff/invoices/{invoice_id} takes notes and due_date, and POST /staff/invoices/{invoice_id}/cancel takes an optional reason of up to 500 characters.

POST /staff/invoices/{invoice_id}/charge charges the saved payment method and answers the outcome in status: succeeded, pending, requires_action, failed with an error_message, skipped with a reason of auto_pay_disabled or payment_settling, or no_method. POST /staff/invoices/{invoice_id}/send answers whether the email was sent and its template, invoice_created or invoice_overdue.

Shell
curl -X POST https://api.coritan.com/api/v1/orgs/acme/staff/invoices/3051/mark-paid \
  -H "Authorization: Bearer $STAFF_TOKEN"
JSON
{"message": "Invoice marked as paid"}
Shell
curl -X POST https://api.coritan.com/api/v1/orgs/acme/staff/invoices/3051/refund \
  -H "Authorization: Bearer $STAFF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount": "10.00", "reason": "Charged twice for the same month", "to": "gateway_refund"}'

to is gateway_refund (the default) or wallet_credit. The answer has the request we recorded and the outcome: status is approved with the amount refunded and any service_status, or failed with the gateway's error. A refund the policy refuses answers 400 with the reason.

Shell
curl "https://api.coritan.com/api/v1/orgs/acme/staff/refund-requests?status=pending" \
  -H "Authorization: Bearer $STAFF_TOKEN"

status is pending (the default), approved, rejected, withdrawn, failed or all, and limit takes 1–200 (50 by default). The answer has the pending count, the requests in items, oldest first, and can_decide, which says whether you may decide them. Each request carries its eligibility, with in_window, flags and blockers.

Shell
curl -X POST https://api.coritan.com/api/v1/orgs/acme/staff/refund-requests/77/approve \
  -H "Authorization: Bearer $STAFF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"resolution": "wallet_credit", "note": "Credited as the renewal was outside the window"}'

resolution is gateway_refund (the default) or wallet_credit, and note takes up to 2,000 characters. POST /staff/refund-requests/{request_id}/reject takes a note of 5–2,000 characters, which goes in the customer's email. A missing request answers 404 Refund request not found.

Read payments, failed charges and disputes

Section titled Read payments, failed charges and disputes
Shell
curl "https://api.coritan.com/api/v1/orgs/acme/staff/transactions?type=refund&since=2026-09-01" \
  -H "Authorization: Bearer $STAFF_TOKEN"

type is payment, refund, chargeback, credit_add or credit_deduct; any other answers 400 Unknown transaction type. gateway takes a gateway's name, q up to 120 characters, since and until dates, limit 1–200 (50 by default) and offset. GET /staff/transactions/stats?days=30 counts and totals each type over 1–365 days.

GET /staff/dunning lists the charge schedules of open invoices, the next attempt first, each with status, attempt_count, max_attempts, next_attempt_at, give_up_at and last_error, and the invoice and customer. status narrows the list to active or paused schedules. GET /staff/dunning/stats answers the counts and what the invoices still owe in owed_by_currency. GET /staff/disputes takes limit and offset, and answers each dispute with its customer and invoice_number.

GET /staff/transactions/export.csv takes type, gateway, since and until. GET /staff/invoices/export.csv takes status, since and until: here the filter is status, where the list takes status_filter.

Shell
curl "https://api.coritan.com/api/v1/orgs/acme/staff/orders?status_filter=stuck" \
  -H "Authorization: Bearer $STAFF_TOKEN"

status_filter is an order status or stuck, and q, customer_id, audience, limit and offset work as they do for invoices. Each order has hostname, status, billing_cycle, amount, next_due_date, server_uuid, waiting_seconds and unpaid, which is true while its invoice waits for payment. GET /staff/orders/stats counts each status and stuck, and GET /staff/orders/{service_id} answers the order, the customer, up to 20 invoices and the server.

POST /staff/services/{service_id}/retry-provision answers status provisioning and the job_id. The service actions are POST routes on /staff/services/{service_id}:

Route Body What it does
/suspend reason up to 300 characters, notify (default true) Suspends the service.
/unsuspend None Lifts the suspension, and answers deletion_cancelled.
/cancel immediate (default false), reason up to 500 characters, keep_snapshot (default true) Cancels as the customer would. GET /cancel-preview?immediate=true quotes the credit first.
/terminate reason of 3–500 characters, notify_customer (default false) Ends the service now, with no credit.
/change-plan org_pricing_id Moves the service to that plan. GET /plan-options lists the plans it can move to, and GET /plan-preview?org_pricing_id= quotes the change.
/cancel-plan-change None Calls off an upgrade waiting for payment.
/notes body of 1–8,000 characters Adds a note and answers 201. GET /notes lists them, newest first.

PATCH /staff/services/{service_id}/due-date takes next_due_date and a reason of 3–500 characters, and answers the next_due_date and previous_due_date.

Shell
curl -X POST https://api.coritan.com/api/v1/orgs/acme/staff/coupons \
  -H "Authorization: Bearer $STAFF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "WELCOME10", "kind": "percent", "value": "10", "max_redemptions": 500, "ends_at": "2026-12-31T23:59:00Z"}'

kind is percent (the default) or fixed, which needs a currency. description takes up to 200 characters, applies_to a list of product IDs, max_redemptions 1 or more, per_customer_limit 0–100 (1 by default), and status active (the default) or paused. first_invoice_only is kept with the coupon, and changes nothing: every coupon comes off the first invoice only. It answers 201 with the coupon.

PATCH /staff/coupons/{coupon_id} changes the description, value, applies_to, max_redemptions, per_customer_limit, dates or status, which may also be expired; clear_applies_to, clear_max_redemptions and clear_ends_at remove a limit. The code, kind and currency stay as they are. POST /staff/coupons/feature with {"enabled": true} turns coupons on. GET /staff/coupons takes status and answers enabled, your products, and up to 300 coupons, each with its redemption_count and total_discounted. GET /staff/coupons/{coupon_id}/redemptions lists up to 500 uses, 100 by default.

API operations on this page

MethodPathWhat it does
GET/api/v1/orgs/{org_slug}/staff/invoicesStaff invoices
POST/api/v1/orgs/{org_slug}/staff/invoicesRaise a one-line invoice by hand: a setup fee, an add-on, a correction
GET/api/v1/orgs/{org_slug}/staff/invoices/export.csvEvery invoice in the window, as a CSV
GET/api/v1/orgs/{org_slug}/staff/invoices/statsStaff invoices stats
GET/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}Staff invoice hub
PATCH/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}Staff patch invoice
POST/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}/cancelClose an invoice nobody should pay
POST/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}/chargeStaff charge invoice
POST/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}/mark-paidStaff mark invoice paid
GET/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}/pdfThe same document the customer downloads from their portal
POST/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}/refundStaff refund invoice
POST/api/v1/orgs/{org_slug}/staff/invoices/{invoice_id}/sendStaff send invoice
GET/api/v1/orgs/{org_slug}/staff/transactionsEvery payment, refund and wallet movement the brand has recorded
GET/api/v1/orgs/{org_slug}/staff/transactions/export.csvThe transactions list as it is filtered, as a CSV, for the books
GET/api/v1/orgs/{org_slug}/staff/transactions/statsTotals per type over the window, for the strip above the list
GET/api/v1/orgs/{org_slug}/staff/refund-requestsStaff refund requests
GET/api/v1/orgs/{org_slug}/staff/refund-requests/{request_id}Staff refund request
POST/api/v1/orgs/{org_slug}/staff/refund-requests/{request_id}/approveStaff approve refund request
POST/api/v1/orgs/{org_slug}/staff/refund-requests/{request_id}/rejectStaff reject refund request
GET/api/v1/orgs/{org_slug}/staff/disputesStaff disputes
GET/api/v1/orgs/{org_slug}/staff/dunningStaff dunning
GET/api/v1/orgs/{org_slug}/staff/dunning/statsHow many invoices the retry job holds in each state, and what they still owe
GET/api/v1/orgs/{org_slug}/staff/couponsStaff list coupons
POST/api/v1/orgs/{org_slug}/staff/couponsStaff create coupon
POST/api/v1/orgs/{org_slug}/staff/coupons/featureTurn coupons on or off for the brand
PATCH/api/v1/orgs/{org_slug}/staff/coupons/{coupon_id}Staff patch coupon
GET/api/v1/orgs/{org_slug}/staff/coupons/{coupon_id}/redemptionsStaff coupon redemptions
GET/api/v1/orgs/{org_slug}/staff/ordersStaff orders
GET/api/v1/orgs/{org_slug}/staff/orders/statsStaff orders stats
GET/api/v1/orgs/{org_slug}/staff/orders/{service_id}Staff order hub
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/cancelStaff cancel service
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/cancel-plan-changeStaff cancel plan change
GET/api/v1/orgs/{org_slug}/staff/services/{service_id}/cancel-previewStaff cancel preview
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/change-planMove the service to another plan of the same cycle
PATCH/api/v1/orgs/{org_slug}/staff/services/{service_id}/due-dateMove the next renewal
GET/api/v1/orgs/{org_slug}/staff/services/{service_id}/notesStaff service notes
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/notesStaff add service note
GET/api/v1/orgs/{org_slug}/staff/services/{service_id}/plan-optionsStaff plan options
GET/api/v1/orgs/{org_slug}/staff/services/{service_id}/plan-previewStaff plan preview
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/retry-provisionRe-queue provisioning for an order stuck before it had a server
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/suspendStaff suspend service
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/terminateEnd a service now with no cancellation credit
POST/api/v1/orgs/{org_slug}/staff/services/{service_id}/unsuspendStaff unsuspend service