Contacts & Invoices write API
Create / update / archive / delete Contacts and draft Invoices via x0 → Xero Accounting API. Read paths remain OData / cache tables.
Scopes
| Scope | Use |
|---|---|
xero.contacts.write |
PUT/POST/DELETE /v1/contacts… and OData Contacts writes |
xero.invoices.write |
PUT/POST/DELETE /v1/invoices… and OData Invoices writes |
Demo customers: writes rejected (CustomerAccessDenied). Successful mutations mark contacts / invoices (+ *_table) cache dirty.
Upstream Xero OAuth on the connected app still needs accounting.contacts / accounting.transactions (not only the x0 API-key scopes).
REST routes (primary)
Contacts
| Method | Path | Behaviour |
|---|---|---|
POST |
/v1/contacts/preflight |
{action, contact_id?, payload?} |
PUT |
/v1/contacts |
Create — require Name |
POST |
/v1/contacts/{contact_id} |
Update (field allow-list) |
POST |
/v1/contacts/{contact_id}/archive |
ContactStatus=ARCHIVED |
DELETE |
/v1/contacts/{contact_id} |
Delete after rules |
Invoices (draft-first)
| Method | Path | Behaviour |
|---|---|---|
POST |
/v1/invoices/preflight |
{action, invoice_id?, payload?} |
PUT |
/v1/invoices |
Create — require Type, Contact, ≥1 LineItems; default Status=DRAFT |
POST |
/v1/invoices/{invoice_id} |
Update draft fields |
DELETE |
/v1/invoices/{invoice_id} |
Delete drafts only |
Not in v1: payment, allocation — use credit-notes-payments-write.md for authorise/void/payments.
x0 REST uses PUT for create (Accounts-style). Upstream Xero: Contacts use PUT create / POST update; Invoices use POST for both create and update.
OData aliases
| Method | Path | Maps to |
|---|---|---|
POST |
/odata/v1/Contacts |
create |
PATCH / PUT |
/odata/v1/Contacts/{id} |
update |
DELETE |
/odata/v1/Contacts/{id} |
delete |
Same for Invoices |
Require matching xero.*.write scopes. Line tables (Contacts_Table, Invoices_Table) return 405. No $batch / deep insert this wave.
Preflight / write rules
evaluate_write_rules seeds cover Contacts and Invoices required fields. Call /v1/.../preflight before delete/archive.
Implementation
| Piece | Where |
|---|---|
| Shared helpers | portal/services/write_common.py |
| Contacts | portal/services/contacts_write.py · api/routers/contacts.py |
| Invoices | portal/services/invoices_write.py · api/routers/invoices.py |
| OData | api/odata/writes.py |
| Accounts (existing) | accounts-write.md |
Simulator
xerosim accepts PUT/POST/DELETE for Contacts and Invoices only (upsert SimResource). Other resources stay 403.
Follow-ups
- Excel VBA macros (Accounts-style write sheets)
- CreditNotes — see credit-notes-payments-write.md
- Invoice authorise / void / payments — see credit-notes-payments-write.md
Related: odata.md · accounts-write.md · items-purchase-orders-write.md · xero-simulator.md