Calender Pen iconMarch 27, 2026ยทguides

API-First Invoicing: Why Developers Choose Corinthian

Corinthian's REST API lets you create invoice shells, manage clients, record payments, and connect invoice data to internal systems.

API-First Invoicing: Why Developers Choose Corinthian

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_KEY

API 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 client

Client 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 invoice

The 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 payment

Payments 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 sequence

Sequences 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_456

Common filters:

  • status -- Filter by invoice status (draft, sent, paid, overdue, void)
  • client_id -- Filter by client
  • created_after / created_before -- Date range filters
  • amount_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 -- Success
  • 201 -- Resource created
  • 400 -- Bad request (validation error)
  • 401 -- Unauthorized (invalid or missing API key)
  • 403 -- Forbidden (insufficient permissions)
  • 404 -- Resource not found
  • 422 -- Unprocessable entity (business logic error)
  • 429 -- Rate limited
  • 500 -- 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: 1679443200

If 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-03

Metadata

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

  1. Generate an API key in Settings > API Keys
  2. Make your first request -- list your clients: GET /v1/clients
  3. Create a test invoice shell -- POST /v1/invoices with type and status
  4. Record a payment -- POST /v1/payments with the invoice ID and payment method
  5. 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.

Thanks for reading.

We use cookies to improve your experience, analyze traffic, and personalize content.