Invoices
A sales invoice (crm_invoices) with line items, VAT, payment tracking, and Stripe hosted invoice / payment link integration. Scopes: sales.invoices.read / sales.invoices.write / sales.invoices.delete.
Endpoints
/api/workspace/v1/invoices/api/workspace/v1/invoices/{id}/api/workspace/v1/invoices/api/workspace/v1/invoices/{id} (draft only)/api/workspace/v1/invoices/{id}/send/api/workspace/v1/invoices/{id}/mark-paid/api/workspace/v1/invoices/{id}/void/api/workspace/v1/invoices/{id}/credit-note/api/workspace/v1/invoices/{id}/send-reminder/api/workspace/v1/invoices/{id} (draft only)/api/workspace/v1/invoices/{id}/restore/api/workspace/v1/invoices/bulk-deleteThe invoice object
| Field | Type | Description |
|---|---|---|
| crm_contact_id* | integer | Contact the invoice is addressed to (required on create) |
| crm_deal_id | integer | Optional linked CRM deal |
| lines* | array | 1-100 line items — see line schema below |
| notes | string | Free-form notes on the invoice (<= 5000 chars) |
| payment_terms_days | integer | Payment terms in days (1-365, default 14). Sets due_at at create time. |
| discount_amount | number | Absolute discount amount |
| discount_reason | string | Short reason shown on the invoice |
| manual_label | string | Optional label (e.g. project ref) |
| reference | string | Free-form PO / reference number |
| currency | string(3) | ISO 4217 (defaults to workspace currency) |
| status | enum | draft | sent | overdue | paid | cancelled | void | credit_note (read-only) |
| credit_note_for_id | integer | Set on credit-notes only. References the original invoice. |
The line item
| Field | Type | Description |
|---|---|---|
| description* | string | 1-500 chars |
| quantity* | number | Positive quantity |
| unit_price* | number | Non-negative unit price |
| vat_rate | number | VAT percentage 0-100 (default 21) |
| price_includes_vat | boolean | Default false |
| discount_percent | number | Per-line % discount (0-100) |
| product_id | integer | Optional catalog product id |
| sort_order | integer | Display order (default insert order) |
Create a draft invoice
Invoices are created in draft status. Totals (subtotal, VAT, total) are calculated server-side from the lines and returned in the response.
curl "https://app.staffifyai.com/api/workspace/v1/invoices" \
-X POST \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"crm_contact_id": 1893,
"crm_deal_id": 1024,
"payment_terms_days": 14,
"reference": "PO-2026-0912",
"lines": [
{ "description": "AI Voice Agent — Sept", "quantity": 1, "unit_price": 499.00, "vat_rate": 21 },
{ "description": "Onboarding + setup", "quantity": 1, "unit_price": 299.00, "vat_rate": 21 }
]
}'Send the invoice
POST /invoices/{id}/send transitions draft → sent. If the workspace has Stripe Connect + the recipient has an email, Stripe Invoicing hosts the invoice email itself; otherwise we fall back to a Stripe Payment Link and send a Staffify SES email. A public accept URL is always returned as public_url.
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/551/send" \ -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \ -H "Idempotency-Key: send-551-2026-09-16"
Mark as paid (manual/bank/cash)
Records a row in the payments audit trail and updates amount_paid. If the cumulative amount covers the total (including any deposit), status flips to paid. Partial payments leave status at sent.
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/551/mark-paid" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 949.00,
"method": "bank_transfer",
"reference": "SEPA-2026-09-16",
"paid_at": "2026-09-16T10:30:00Z"
}'Void or credit-note
void is for unpaid invoices — the invoice is cancelled and (if applicable) Stripe is instructed to void the hosted invoice. Paid invoices cannot be voided; issue a credit-note instead which creates a NEW invoice with negative amounts and credit_note_for_id pointing back at the original.
# Void (unpaid only)
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/551/void" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Customer withdrew order" }'
# Credit-note (returns the NEW invoice)
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/551/credit-note" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Partial refund for missed delivery" }'Send a reminder
Manually trigger a reminder email for sent or overdue invoices. Rate-limited to once per hour per invoice (returns 422 with "A reminder was already sent in the last hour" if throttled).
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/551/send-reminder" \ -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"
List invoices
Filter by status, deal_id, contact_id. Set overdue=true to select sent invoices past their due_at. Recycle-bin filter via deleted=true | false | any.
curl "https://app.staffifyai.com/api/workspace/v1/invoices?status=sent&overdue=true" \ -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"
Delete / restore / bulk-delete
Only draft invoices can be soft-deleted. Sent/paid invoices need to be voided or credit-noted for audit trail. Soft-deleted drafts move to a 30-day recycle bin; POST /:id/restore puts them back. Requires the separate sales.invoices.delete scope.
# Soft-delete draft
curl -X DELETE "https://app.staffifyai.com/api/workspace/v1/invoices/551" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"
# Restore
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/551/restore" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"
# Bulk-delete (max 500 draft ids; non-draft ids silently skipped)
curl -X POST "https://app.staffifyai.com/api/workspace/v1/invoices/bulk-delete" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": [551, 552, 553] }'Webhooks
Invoice mutations fire the following events (subscribe via webhook endpoints):
invoice.createdinvoice.updatedinvoice.sentinvoice.paidinvoice.voidedinvoice.credit_note_issuedinvoice.reminder_sentinvoice.deletedinvoice.restored