Overview
The Nilo Headless API lets external programs drive Nilo capabilities without requiring the browser UI or chat interface. The target operating model is a customer-specific middleware process running on a controlled host that calls Nilo over a stable HTTP API.
This is effectively a headless mode for Nilo. It is intended for:
- AI agents and autonomous systems that need to create invoices, send messages, or query business state on behalf of a tenant.
- Customer-hosted middleware that keeps raw or sensitive source data local and sends only structured commands to Nilo.
- Operator-managed integrations that sync ERP data, import contacts, or trigger assistant workflows.
- Embedded partner applications that use Nilo as an AI workflow, communication, or content generation backend.
Base URL
https://app.nilo-assistant.com
All paths in this document are relative to this base.
Authentication
All API requests authenticate with a per-application bearer token. The token resolves the tenant and application identity. There is no browser session or cookie involved.
Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx
Tenant admins create application credentials inside Nilo. Each credential carries a name, a key fingerprint for identification, and an active or revoked status. Keys can be rotated and revoked at any time.
Request And Response Conventions
- Content type:
application/json for both requests and responses.
- Resource paths: path segments for direct resource access; no query parameters for new endpoints.
- Search and list:
POST-based search resources with JSON request bodies carrying filters, sort, page size, and cursor.
- Long-running work: returns
202 Accepted with a job resource for polling.
- Idempotency: include
Idempotency-Key: <opaque-key> on side-effecting create and update operations.
- Correlation: optional
X-Correlation-Id: <opaque-id> for tracing requests across systems.
Error Contract
Errors use a consistent RFC 7807-style problem-details shape:
{
"type": "https://nilo-assistant.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "At least one item is required.",
"code": "invalid_request",
"correlationId": "8f5f7d90-6e0c-4c82-95c9-1ef85d221f1d"
}
Common HTTP status codes:
- 400 Bad Request — malformed payload or validation failure.
- 401 Unauthorized — missing, malformed, or invalid bearer token.
- 403 Forbidden — valid token but the feature is not enabled for the tenant.
- 404 Not Found — resource does not exist or is not accessible.
- 409 Conflict — idempotency key collision or concurrent modification.
- 422 Unprocessable Entity — semantic business-rule violation.
Hello (Connection Check)
A simple endpoint to verify that your API key is valid and see which application identity it resolves to. Use this to confirm connectivity before making further calls.
Check Connection
GET /api/v1/hello
Verify the bearer token and return the resolved application identity.
curl https://app.nilo-assistant.com/api/v1/hello \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx"
Response
Returns 200 OK on success:
{
"status": "ok",
"applicationId": "app_01XYZ789",
"applicationName": "Runner"
}
Returns 401 Unauthorized if the token is missing, malformed, or revoked.
Invoices
The Invoice API is the first production headless domain. It creates invoices from an authenticated application and returns machine-friendly JSON. This is the template for future headless domains.
The accounting feature must be enabled for the tenant. If the feature is disabled, the endpoint returns 403 Forbidden with the error feature_not_enabled.
Create Invoice
POST /api/v1/invoices
Create a new draft invoice for the tenant associated with the bearer token.
curl -X POST https://app.nilo-assistant.com/api/v1/invoices \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inv-2026-05-30-001" \
-d '{
"customer": "Acme Floral Supplies",
"customerContactId": "contact_abc123",
"customerCompany": "Acme Floral Supplies LLC",
"customerAddress": "123 Garden Lane, Portland, OR 97201",
"customerVatId": null,
"reference": "PO-45678",
"paymentInstructions": "Net 30 via bank transfer",
"note": "Seasonal bulk order",
"issuedDate": "2026-05-30",
"paymentTermsDays": 30,
"items": [
{
"description": "Premium rose stems (500 count)",
"quantity": 2,
"price": 149.99,
"taxRate": 8.5
},
{
"description": "Delivery fee",
"quantity": 1,
"price": 25.00,
"taxRate": 0
}
]
}'
Request Fields
- customer
string — display name of the billed party. Required.
- customerContactId
string | null — Nilo contact ID to link the invoice to an existing contact.
- customerCompany
string | null — formal company name for the invoice.
- customerAddress
string | null — billing address.
- customerVatId
string | null — VAT or tax identification number.
- reference
string | null — external reference such as a purchase order.
- paymentInstructions
string | null — payment terms or instructions.
- note
string | null — internal or display note.
- issuedDate
string (ISO-8601 date) — invoice issue date. Required.
- paymentTermsDays
integer — days until due. Required.
- items
array — at least one line item is required.
Line Item Fields
- description
string — item description. Required.
- quantity
number — quantity. Required.
- price
number — unit price. Required.
- taxRate
number — tax rate as a percentage (e.g., 8.5 for 8.5%). Required.
Response
Returns 201 Created on success:
{
"invoiceId": "inv_01HABC123DEF456",
"invoiceNumber": "INV-2026-00042",
"status": "draft",
"applicationId": "app_01XYZ789",
"applicationName": "ERP Sync"
}
Contacts
The Contacts API provides CRUD operations for tenant-scoped contacts. Contacts are the shared relationship layer used by communication, invoicing, and other Nilo features.
The communication feature must be enabled for the tenant. If the feature is disabled, the endpoint returns 403 Forbidden with the error feature_not_enabled.
Create Contact
POST /api/v1/contacts/contacts
Create a new contact for the tenant associated with the bearer token.
curl -X POST https://app.nilo-assistant.com/api/v1/contacts/contacts \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: contact-2026-05-30-001" \
-d '{
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com",
"phone": "+1-555-1234",
"company": "Acme Inc",
"streetAddress": "123 Commerce Blvd, Suite 200",
"city": "Portland",
"state": "OR",
"postalCode": "97201",
"country": "US",
"tag": "lead",
"preferredLanguage": "en-US",
"formality": "informal",
"preferredTone": "friendly",
"salutation": "Hi",
"addressingNotes": "Prefers first name",
"note": "Met at trade show"
}'
Request Fields
- email
string | null — primary email address. Must be unique within the tenant when provided.
- firstName
string | null — first name.
- lastName
string | null — last name.
- phone
string | null — phone number.
- company
string | null — company or organization name.
- streetAddress
string | null — street address.
- city
string | null — city or locality.
- state
string | null — state, province, or region.
- postalCode
string | null — postal or ZIP code.
- country
string | null — country name or ISO 3166-1 alpha-2 code.
- tag
string | null — free-form tag for grouping or filtering.
- preferredLanguage
string | null — preferred language tag such as en-US or es-419.
- formality
string | null — formality preference for communication.
- preferredTone
string | null — tone preference for communication.
- salutation
string | null — preferred salutation.
- addressingNotes
string | null — notes on how to address this contact.
- note
string | null — general internal note.
- customData
object | null — arbitrary key-value data stored as JSON. Useful for middleware-specific fields such as external IDs, loyalty tiers, or integration state. Values may be strings, numbers, booleans, nested objects, or arrays.
Response
Returns 201 Created on success:
{
"contactId": "contact_01HABC123DEF456",
"email": "jane@acme.com",
"fullName": "Jane Doe",
"firstName": "Jane",
"lastName": "Doe",
"phone": "+1-555-1234",
"company": "Acme Inc",
"streetAddress": "123 Commerce Blvd, Suite 200",
"city": "Portland",
"state": "OR",
"postalCode": "97201",
"country": "US",
"tag": "lead",
"preferredLanguage": "en-us",
"formality": "informal",
"preferredTone": "friendly",
"salutation": "Hi",
"addressingNotes": "Prefers first name",
"note": "Met at trade show",
"customData": {
"erpId": "ERP-12345",
"loyaltyTier": "gold",
"preferences": {
"newsletter": true,
"sms": false
}
},
"updatedAt": "2026-05-30T12:00:00Z"
}
List Contacts
GET /api/v1/contacts/contacts
List all contacts for the tenant.
curl https://app.nilo-assistant.com/api/v1/contacts/contacts \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx"
Returns 200 OK:
{
"items": [
{
"contactId": "contact_01HABC123DEF456",
"email": "jane@acme.com",
"fullName": "Jane Doe",
"firstName": "Jane",
"lastName": "Doe",
"phone": "+1-555-1234",
"company": "Acme Inc",
"streetAddress": "123 Commerce Blvd, Suite 200",
"city": "Portland",
"state": "OR",
"postalCode": "97201",
"country": "US",
"tag": "lead",
"preferredLanguage": "en-us",
"formality": "informal",
"preferredTone": "friendly",
"salutation": "Hi",
"addressingNotes": "Prefers first name",
"note": "Met at trade show",
"createdAt": "2026-05-30T12:00:00Z",
"updatedAt": "2026-05-30T12:00:00Z"
}
]
}
Get Contact
GET /api/v1/contacts/contacts/{contactId}
Retrieve a single contact by ID.
curl https://app.nilo-assistant.com/api/v1/contacts/contacts/contact_01HABC123DEF456 \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx"
Returns 200 OK with the contact object, or 404 Not Found if the contact does not exist.
Update Contact
PATCH /api/v1/contacts/contacts/{contactId}
Update an existing contact. All fields are optional; omitted fields are left unchanged.
curl -X PATCH https://app.nilo-assistant.com/api/v1/contacts/contacts/contact_01HABC123DEF456 \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"email": "jane@acme.com",
"firstName": "Janet",
"lastName": "Doe",
"phone": "+1-555-9999",
"company": "Acme Inc",
"streetAddress": "456 Enterprise Ave",
"city": "Portland",
"state": "OR",
"postalCode": "97201",
"country": "US",
"tag": "customer",
"preferredLanguage": "en-US",
"formality": "formal",
"preferredTone": "professional",
"salutation": "Dear",
"addressingNotes": "Prefers formal address",
"note": "Upgraded to customer after first purchase"
}'
Returns 200 OK with the updated contact object.
Delete Contact
DELETE /api/v1/contacts/contacts/{contactId}
Remove a contact from the tenant.
curl -X DELETE https://app.nilo-assistant.com/api/v1/contacts/contacts/contact_01HABC123DEF456 \
-H "Authorization: Bearer nilo_ak_xxxxxxxxxxxxxxxxxxxxxxxx"
Returns 204 No Content on success, or 404 Not Found if the contact does not exist.
Runners
Runners are lightweight background services that connect your own Linux or macOS machines to Nilo. They let you run AI workloads on hardware you control while still coordinating through Nilo.
Use cases include:
- Data locality: Keep sensitive data on your own servers. The runner processes jobs locally and only sends results back to Nilo.
- Custom models: Run local LLMs such as Llama via Ollama instead of using cloud-hosted models.
- Existing infrastructure: Use machines you already have rather than provisioning new cloud resources.
Nilo provides runners for both Linux and macOS. Installation is via a one-line command. Once installed, the runner runs as a background service, maintains a persistent connection to Nilo, and automatically picks up jobs assigned to it.
The installer and binaries are available from the caimito/nilo-runner repository.
Runners are managed through Nilo and require a registration key. Contact your Nilo representative to enable runner access for your tenant.
Async Model And Webhooks
Long-running operations such as image generation, mailbox sync, and document ingestion return a job resource instead of blocking the request.
{
"jobId": "job_01JX...",
"status": "queued",
"resourceType": "image-generation",
"pollPath": "/api/v1/jobs/job_01JX..."
}
Job statuses are: queued, running, succeeded, failed, canceled.
Webhook subscriptions will be available so middleware can react to events without polling. Event families include assistant.thread.updated, communication.message.created, accounting.invoice.created, media.image.completed, and others.
Data Locality
The API is designed to support customer-hosted middleware where raw source data stays on the customer-controlled host. Supported modes:
- Pass-through: middleware sends structured commands; Nilo stores and operates on the resulting state.
- Local preprocessing: middleware reads raw data locally, extracts only the structured fields Nilo needs, and raw content never leaves the host.
- Hybrid sync: middleware keeps the source of truth locally; Nilo stores mirrored operational state; both sides correlate with external IDs.
What Is Coming Next
The invoice and contacts APIs are the first headless domains. The planned expansion covers the full tenant-facing platform:
- Assistant — thread lifecycle, messages, attachments, tool-backed runs, and structured outputs.
- Projects — project CRUD, content inventory, and active project selection.
- Knowledge — knowledge documents, scope flags, and consumer attachments.
- Communication — mailbox configuration, message search, reply drafts, and outbound send.
- Reminders, Tasks, And Calendar — CRUD and AI-derived follow-up state.
- Media And Content Studio — image and video generation, reference subjects, and publish workflows.
- Files And Archive — uploads, ingestion, search, and attachment retrieval.
- Website And Newsletters — widget configuration, news items, and campaign send jobs.
- Integrations — tenant-facing integration state and channel actions.
Each new domain will follow the same auth model, error contract, and async semantics established here.