Build on your CRM
with the Eutexa API
A versioned REST API for reading and writing your CRM from Make, n8n, Zapier, or your own code. JSON in, JSON out, over HTTPS.
https://api.eutexa.com/api/v1
Your first request
- 1
In Eutexa, open Settings → Developer and click New API key. Only the super administrator and admins can create one.
- 2
Name it after the integration, pick its scopes, and create it. The key is shown once — copy it now, because it is stored only as a hash and can never be shown again.
- 3
Send it as the
X-Api-Keyheader on every request.
Check the key works. /ping needs no scope, so it is always a clean test of the credential itself:
curl https://api.eutexa.com/api/v1/ping \ -H "X-Api-Key: eutx_live_your_key_here"
It answers with the workspace the key belongs to:
{
"success": true,
"data": {
"workspaceId": "6650a1b2c3d4e5f600000099",
"workspace": "Acme Corp",
"email": "you@acme.com",
"connectionLabel": "Acme Corp (you@acme.com)"
}
}Then read some data, or write some:
curl "https://api.eutexa.com/api/v1/contacts?limit=20" \
-H "X-Api-Key: $EUTEXA_API_KEY"
curl -X POST https://api.eutexa.com/api/v1/contacts \
-H "X-Api-Key: $EUTEXA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"firstName":"Jordan","email":"jordan@example.com"}'Seeing /api/api/v1?
The base URL is https://api.eutexa.com/api/v1 — the host serves everything under /api, and this API's own prefix is /v1. If your URL has /api twice, you have appended the prefix to a base that already carried it. Strip one and the call will work.
Two ways to authenticate
Both resolve to the same workspace context, so every endpoint behaves identically whichever you use.
API key — for Make, n8n and your own code
A workspace-scoped secret sent as a header. This is what you want for a server-side integration or a script.
X-Api-Key: eutx_live_xxxxxxxxxxxxxxxxxxxxxxxx
- Shown once at creation and stored only as a hash — it cannot be recovered.
- Limited to the scopes you choose, which cannot be edited afterwards. To change them, create a new key and revoke the old one.
- Active keys per workspace: 2 on Starter, 10 on Pro, unlimited on Enterprise.
- It authenticates as the whole workspace and carries no role, which is why creating one needs admin rights.
- It is a secret. Keep it server-side — never in browser code, a mobile app, or a public repository.
OAuth2 — for platforms
Authorization Code grant, used by the official Zapier app. Your platform sends the user to Eutexa to log in, pick a workspace and approve; you receive an access token (valid one hour) and a rotating refresh token.
Authorization: Bearer <access_token> GET https://api.eutexa.com/api/v1/oauth/authorize POST https://api.eutexa.com/api/v1/oauth/token
Clients are registered by us — get in touch for a client id and secret. The grant is a single crm scope; the API-key scopes below do not narrow an OAuth token, because its grant is agreed at authorize time and the user approves it in a browser. Access can be revoked from Eutexa settings at any time.
What a key is allowed to reach
Every key carries the scopes you picked when you created it. Call an endpoint the key does not cover and you get 403 INSUFFICIENT_SCOPE, with the missing scope named in requiredScope so you know exactly what to add.
A scope narrows which endpoints, never which records: a key with contacts:readreads every contact in the workspace. Keys created before scopes existed keep full access and show as “Full access” in Developer settings.
Grant these three sparingly
email:send and sequences:write send mail to your contacts from your own mailbox, and ai:run spends AI credits. Everything else only moves data inside your CRM.
Read
| contacts:read | List, search and fetch contacts, including their email, phone and custom fields. |
| companies:read | List, search and fetch companies. |
| opportunities:read | List and fetch deals, including their value and stage-change history. |
| tasks:read | List and fetch tasks and their assignees. |
| tickets:read | List and fetch support tickets. |
| activities:read | Read the timeline: notes, calls, meetings and logged emails. |
| products:read | List, search and fetch products and their prices. |
| invoices:read | List and fetch invoices and their totals. |
| meetings:read | List and fetch meetings and their summaries. |
| sequences:read | List email sequences. |
| forms:read | Read submissions to your forms, including the raw answers. |
| email:read | Read replies received against email you sent from Eutexa. |
| metadata:read | Read pipeline and stage names plus workspace member names and emails, for dropdowns. |
Write
| contacts:write | Create and update contacts. Counts against your plan contact limit. |
| companies:write | Create companies. |
| opportunities:write | Create deals in any pipeline. |
| tasks:write | Create and update tasks. |
| tickets:write | Create support tickets. |
| activities:write | Log notes and activities onto a contact or deal timeline. |
| products:write | Create products in the catalog. |
| sequences:write | Enroll a contact into a sequence, which sends them email on your schedule. |
| email:send | Send email from your connected mailbox. Counts against your monthly send limit. |
| ai:run | Run Co-Pilot, generate email copy and enrich contacts. Spends AI credits from your workspace pool. |
The same rules on every endpoint
- Newest first. Lists are ordered so a polling integration sees new records at the top.
- Stable ids. Every record carries a top-level
id. Use it to de-duplicate. - Paging.
limitis 1 to 100 (default 50),pageis 1-based. - Incremental pulls.
sincetakes an ISO 8601 timestamp and returns records at or after it. Addupdated=trueto work from last-updated instead of created. - Junk ids are ignored. An unparseable record id in a query parameter is dropped rather than erroring, so a stray dropdown value cannot break a request.
- One error shape. Failures return
{ success: false, error: { code, message } }with a meaningful status. Stack traces are never returned.
Every route in v1
All paths are relative to https://api.eutexa.com/api/v1. The scope column is what an API key must hold to call it.
Contacts
| GET | /contacts | contacts:read | List contacts, newest first. |
| GET | /contacts/search | contacts:read | Find by ?email= — pairs with create for find-or-create. |
| GET | /contacts/:id | contacts:read | Fetch one contact. |
| POST | /contacts | contacts:write | Create a contact. Dedupes by email, then phone. |
| PUT | /contacts/:id | contacts:write | Update a contact. All fields optional. |
Companies
| GET | /companies | companies:read | List companies. |
| GET | /companies/search | companies:read | Find by ?domain= or ?name=. |
| GET | /companies/:id | companies:read | Fetch one company. |
| POST | /companies | companies:write | Create a company. Domain is derived from the website. |
Opportunities
Deals. Omit pipelineId and stageId to use the workspace default pipeline.
| GET | /opportunities | opportunities:read | List deals. Filter by ?status= or ?pipelineId=. |
| GET | /opportunities/changes | opportunities:read | Each stage transition as a dedupable record. |
| GET | /opportunities/:id | opportunities:read | Fetch one deal. |
| POST | /opportunities | opportunities:write | Create a deal. |
Tasks
| GET | /tasks | tasks:read | List tasks. |
| GET | /tasks/:id | tasks:read | Fetch one task. |
| POST | /tasks | tasks:write | Create a task. |
| PUT | /tasks/:id | tasks:write | Update a task, including marking it complete. |
Tickets
| GET | /tickets | tickets:read | List support tickets. |
| GET | /tickets/:id | tickets:read | Fetch one ticket. |
| POST | /tickets | tickets:write | Create a ticket. |
Activities
The contact and deal timeline: notes, calls, meetings and logged email.
| GET | /activities | activities:read | List timeline entries. Filter by ?type= or ?contactId=. |
| POST | /activities | activities:write | Log a note or activity onto a record. |
Products
unitPrice is in whole currency units. Invoice and proposal totals use integer cents instead.
| GET | /products | products:read | List catalog products. |
| GET | /products/search | products:read | Find by ?name= or ?sku=. |
| GET | /products/:id | products:read | Fetch one product. |
| POST | /products | products:write | Create a product. |
Invoices
Read-only. Totals and line items are integer cents.
| GET | /invoices | invoices:read | List invoices. Filter by ?status=. |
| GET | /invoices/:id | invoices:read | Fetch one invoice. |
Meetings
Read-only.
| GET | /meetings | meetings:read | List meetings. |
| GET | /meetings/:id | meetings:read | Fetch one meeting and its summary. |
Sequences
Enrolling sends email to the contact on the sequence schedule, and counts against your monthly enrollment limit.
| GET | /sequences | sequences:read | List email sequences. |
| POST | /sequences/:id/enroll | sequences:write | Enroll a contact. |
Form submissions
Read-only. Spam submissions are excluded.
| GET | /form-submissions | forms:read | List submissions. Filter by ?formId=. |
Sending uses your connected mailbox and counts against your monthly send limit.
| POST | /email/send | email:send | Send an email. |
| GET | /email/replies | email:read | Replies received against email sent from Eutexa. |
AI
Every AI endpoint spends credits from your workspace pool and needs a plan that includes AI. On Starter Lite they return PLAN_FEATURE_REQUIRED.
| POST | /ai/run-copilot | ai:run | Run a Co-Pilot turn. 5 credits. |
| POST | /ai/generate-email | ai:run | Draft email copy. 2 credits. |
| POST | /ai/enrich-contact | ai:run | Enrich a contact. 5 credits. |
Metadata
For building dropdowns instead of asking people to paste record ids.
| GET | /pipelines | metadata:read | Pipelines with their stages, default first. |
| GET | /users | metadata:read | Workspace owner and active members, for assignee pickers. |
What a failure looks like
{
"success": false,
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key does not have the \"email:send\" scope.",
"requiredScope": "email:send"
}
}| 400 | VALIDATION_ERROR | Bad or missing input. |
| 401 | UNAUTHORIZED | Credential missing, invalid, expired or revoked. |
| 402 | TRIAL_EXPIRED / PAYMENT_REQUIRED | Subscription inactive. Reads may still work when only past due. |
| 403 | INSUFFICIENT_SCOPE | The key lacks this endpoint's scope. The response names it in requiredScope. |
| 403 | PLAN_LIMIT_REACHED | A plan resource limit was hit. |
| 403 | PLAN_FEATURE_REQUIRED | Your plan does not include this feature. |
| 404 | NOT_FOUND | No such record. |
| 409 | DUPLICATE | A matching record exists. The response carries existingId. |
| 429 | RATE_LIMITED | More than 300 requests in a minute from this IP. |
| 500 | INTERNAL_ERROR | Unexpected server error. |
Staying inside the lines
Rate limits
300 requests per minute per IP across all v1 endpoints, returning 429 beyond it. The budget is per IP rather than per key, so several keys behind one server share it. Read ratelimit-remaining and ratelimit-reset from the response headers instead of guessing.
Versioning
This is v1. New fields are added without notice and are safe to ignore. Anything that would break an existing integration — a rename, a removal — only ever appears under a new prefix such as /api/v2.
Stuck on something?
Send us the endpoint, the status code and the correlation id from the response headers, and we will tell you exactly what happened.