API-First Invoicing: Why Developers Choose Corinthian
Most invoicing platforms treat their API as an afterthought. They build a web UI first, add a few endpoints later, and call it "API access." The result is often uneven: some workflows are documented, others are dashboard-only, and integration teams have to guess where the boundary is.
Corinthian's public API is intentionally narrow and documented. It exposes bearer-token REST resources for the workflows described in the API reference, plus status pages for capabilities that are not public yet.
For developers building billing into SaaS products, internal tools, or custom workflows, that boundary matters. You can build against documented invoice, client, payment, product, recurring, and sequence endpoints without assuming every dashboard capability has a matching public API.
Why API-First Matters for Invoicing
Invoicing touches almost every part of a business: sales closes a deal, operations delivers the service, finance sends the invoice, AR follows up on payment. In most companies, these handoffs are manual -- someone copies data from the CRM into the invoicing tool, someone else manually marks an invoice as sent, someone checks a spreadsheet to see what's overdue.
An API eliminates these manual handoffs. Your CRM creates the invoice when a deal closes. Your project management tool triggers invoicing when a milestone is delivered. Your payment processor marks invoices as paid in real time. Your dunning system sends follow-ups on a schedule without human intervention.
This isn't theoretical. Here are real integration patterns Corinthian developers build:
SaaS usage-based billing: Pull usage data from your product database, calculate the amount due, create and send the invoice automatically at the end of each billing cycle.
CRM-triggered invoicing: When a deal moves to "Closed Won" in your CRM, create a draft invoice shell and attach enough internal context for the finance team to finish the customer-facing document.
Milestone billing for agencies: When a project milestone is marked complete in your project management tool, generate the corresponding invoice.
Follow-up routing based on custom logic: Pull overdue invoice and payment data into your own review queue, then route follow-up decisions through the team workflow you already use.
Multi-system reconciliation: Pull invoice and payment data from Corinthian into your accounting system, ERP, or data warehouse for consolidated reporting.
The REST API
Corinthian exposes a RESTful API that follows standard conventions. If you've worked with the Stripe API, you'll find the patterns familiar.
Authentication
Every API request requires an API key passed in the Authorization header:
Authorization: Bearer YOUR_API_KEYAPI keys are scoped to a team. You can create multiple keys per team with different permission levels:
- Full access: Create, read, update, delete across all resources
- Read only: List and retrieve resources, no mutations
- Invoice management: Create and manage invoices and clients, no access to settings or team management
Keys are managed in Settings > API Keys. Rotate keys regularly and revoke any key that may have been compromised.
Core Resources
The API is organized around these primary resources:
Clients
POST /v1/clients # Create a client
GET /v1/clients # List clients
GET /v1/clients/:id # Get a client
PATCH /v1/clients/:id # Update a client
DELETE /v1/clients/:id # Delete a clientClient records include name, email, billing address, notes, and references your integration needs for matching records across systems.
Invoices
POST /v1/invoices # Create an invoice
GET /v1/invoices # List invoices
GET /v1/invoices/:id # Get an invoice
PATCH /v1/invoices/:id/status # Update invoice status
POST /v1/invoices/:id/mark-paid # Mark an invoice paid
POST /v1/invoices/:id/refund # Refund an invoice
DELETE /v1/invoices/:id # Delete an invoiceThe create endpoint initializes a minimal invoice shell. Treat status transitions and payment actions as API-controlled behavior, then use the dashboard and documented invoice flows for the full customer-facing document.
Payments
POST /v1/payments # Record a payment
GET /v1/payments # List payments
GET /v1/payments/:id # Get a paymentPayments are linked to invoices. You can record manual payments (check, wire transfer) or let the Stripe integration handle payment recording automatically.
Sequences
GET /v1/sequences # List sequences
POST /v1/sequences/next # Get the next value
PATCH /v1/sequences # Update sequence settings
GET /v1/sequences/preview # Preview next values
POST /v1/sequences/reset # Reset a sequenceSequences manage numbered document flows. Public workflow orchestration is not documented as a workflow API; use the documented reminder, recurring, template, invoice, and sequence APIs instead of assuming a workflow trigger endpoint exists.
Pagination and Filtering
List endpoints support cursor-based pagination and filtering:
GET /v1/invoices?status=overdue&client_id=cli_123&limit=25&cursor=inv_456Common filters:
status-- Filter by invoice status (draft, sent, paid, overdue, void)client_id-- Filter by clientcreated_after/created_before-- Date range filtersamount_min/amount_max-- Amount range filters
Pagination: Every list response includes a next_cursor field. Pass it as the cursor parameter to get the next page. When next_cursor is null, you've reached the end.
Error Handling
The API uses standard HTTP status codes:
200-- Success201-- Resource created400-- Bad request (validation error)401-- Unauthorized (invalid or missing API key)403-- Forbidden (insufficient permissions)404-- Resource not found422-- Unprocessable entity (business logic error)429-- Rate limited500-- Internal server error
Error responses include a structured body:
{
"error": {
"code": "invoice_already_paid",
"message": "Cannot void an invoice that has already been paid",
"details": {
"invoice_id": "inv_abc123",
"current_status": "paid"
}
}
}Rate Limiting
Rate limits vary by endpoint group. The public docs currently list budgets such as 200/minute for analytics and reports, 60/minute for payment and reminder writes, 10/minute for batch work, and 100/minute for most other protected endpoints.
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1679443200If you hit the rate limit, the API returns 429 Too Many Requests. Implement exponential backoff in your client.
Webhook Availability
Customer-managed webhook subscriptions are not described as a public API in the current docs. Corinthian uses webhook endpoints for connected services, such as Stripe and mail providers, but the public API reference does not document a customer-configurable webhook endpoint.
For now, build integrations around documented REST reads, exports, and the availability notes in the API docs. If you need event-style behavior, poll the smallest resource set you need and back off according to the rate-limit page.
Real-World Integration Examples
The snippets below assume you have a small internal wrapper around direct REST calls. They are not examples of an official Corinthian SDK package.
Automated Monthly Billing
// Pull active subscriptions from your database
const activeSubscriptions = await db.subscriptions.findMany({
where: { status: 'active' }
});
for (const sub of activeSubscriptions) {
// Calculate amount based on usage
const usage = await getMonthlyUsage(sub.customerId);
const amount = calculateBillingAmount(sub.plan, usage);
// Create a draft invoice shell for finance review
const invoice = await corinthian.invoices.create({
type: "invoice",
status: "draft",
metadata: {
billing_cycle: format(new Date(), "yyyy-MM"),
source_subscription_id: sub.id,
suggested_amount: amount,
},
});
}CRM Integration (Deal Closed -> Invoice)
// Webhook handler for CRM "deal closed" event
app.post('/webhooks/crm/deal-closed', async (req, res) => {
const deal = req.body;
// Find or create client in Corinthian
let client = await corinthian.clients.findByEmail(deal.contact.email);
if (!client) {
client = await corinthian.clients.create({
name: deal.contact.name,
email: deal.contact.email,
metadata: { crm_deal_id: deal.id },
});
}
// Create a draft invoice shell from deal details
await corinthian.invoices.create({
type: "invoice",
status: "draft",
metadata: {
crm_deal_id: deal.id,
client_id: client.id,
payment_terms_days: deal.paymentTermsDays,
},
});
res.status(200).send('OK');
});Overdue Invoice Review Job
const overdueInvoices = await corinthian.invoices.list({
status: "overdue",
limit: 50,
});
for (const invoice of overdueInvoices.data) {
const paymentHistory = await corinthian.payments.list({
client_id: invoice.client_id,
limit: 10,
});
const onTimePayments = paymentHistory.filter(p => p.paid_on_time);
const onTimeRate = onTimePayments.length / paymentHistory.length;
await notifyFinanceTeam({
invoiceId: invoice.id,
priority: invoice.amount > 10_000 ? "high" : "normal",
recommendedTone: onTimeRate > 0.8 ? "gentle" : "direct",
});
}Developer Experience Details
Idempotency
Documented create endpoints can use an Idempotency-Key header when you need retry-safe writes. If you retry a request with the same key, the integration can avoid creating a duplicate resource. Use this for operations that might be retried after network timeouts or queue replays.
POST /v1/invoices
Idempotency-Key: create-invoice-deal-123-2026-03Metadata
Every resource supports a metadata field -- a JSON object where you can store arbitrary key-value pairs. Use it to link Corinthian resources to your internal systems:
{
"metadata": {
"crm_deal_id": "deal_456",
"project_id": "proj_789",
"billing_cycle": "2026-03"
}
}Metadata is searchable via the API, making it easy to find Corinthian resources by your internal identifiers.
Sandbox Environment
Corinthian API keys use ck_test_ for test work and ck_live_ for live data. Keep test and live keys separate in your own environments.
The public docs do not describe an isolated sandbox with public webhooks or email simulation. Treat test-prefixed keys as the documented testing path unless the API reference says otherwise.
Getting Started
- Generate an API key in Settings > API Keys
- Make your first request -- list your clients:
GET /v1/clients - Create a test invoice shell --
POST /v1/invoiceswithtypeandstatus - Record a payment --
POST /v1/paymentswith the invoice ID and payment method - Build your integration -- start with the simplest flow and expand only through documented endpoints
The API reference documentation is available at docs.conduitt.io. It includes the current public endpoints, error guidance, rate-limit notes, and availability pages for capabilities that are not public yet.
Get your API key and start building with the public REST API documented today.
