# Datalyse API — full reference Datalyse is a multi-tenant CRM and communication platform. Base URL for all endpoints: https://api.datalyse.io This reference contains only endpoints explicitly enabled for AI assistants and MCP tools. Module-specific legacy CRUD for contacts/companies, opportunities, tasks and tickets is intentionally omitted; use the separate unified-by-entity `/api/1.0/entities/*` endpoints for each operation. Outbound communication tools listed here are enabled and must use exactly the destination and content explicitly requested by the user. ## Authentication Authenticated endpoints require the API access token in `token` (query string for GET, JSON body for ordinary POST endpoints, or multipart text field for uploads). Get your token by signing in at /devs/ (OAuth) — it is personal and scopes every request to its company/user. Machine-readable alternative: OpenAPI 3.1 spec at /devs/openapi.json ## Companies ### add_note_to_company `POST https://api.datalyse.io/api/1.0/companies/addnote.json` Add a note to a company | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | company_lead_id | string | yes | ID of the company | | | text | string | yes | Text of the note | Hello world | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companies/addnote.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","company_lead_id":"","text":"Hello world"}' ``` ### get_company_activity `POST https://api.datalyse.io/api/1.0/companies/getactivity.json` Get activity/timeline for a company (includes activity from contacts inside the company) | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | company_lead_id | string | yes | ID of the company | | | type | string | no | Filter by activity type (optional) | | | var_resultspage | string | yes | Maximum number of results to display | 20 | | var_page | string | yes | Page number | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companies/getactivity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","company_lead_id":"","var_resultspage":"20","var_page":"1"}' ``` ## Companyuserdata ### get_company_properties `POST https://api.datalyse.io/api/1.0/companyuserdata/companyproperties.json` Get all statuses, and additional data for company | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/companyproperties.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### get_all_agents `POST https://api.datalyse.io/api/1.0/companyuserdata/getallagents.json` Get all agents for the company | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/getallagents.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### get_price_per_credit `POST https://api.datalyse.io/api/1.0/companyuserdata/getpricepercredit.json` Get price per credit for VOIP calls and SMS, balance and plan info | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/getpricepercredit.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### get_agents_types `POST https://api.datalyse.io/api/1.0/companyuserdata/agentstypes.json` Get all agent types (roles) for the company, including the number of agents assigned to each type | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/agentstypes.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### get_callerids `POST https://api.datalyse.io/api/1.0/companyuserdata/callerids.json` List all outbound caller IDs available to the company, filtered by permissions. Returns every line with host, id, campaign, country, alias, state and costs. The host value is the caller_id accepted by the enabled AI-agent call creation endpoint. Return every row when the user asks for caller IDs. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/callerids.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### invoices_get `POST https://api.datalyse.io/api/1.0/companyuserdata/invoicesget.json` Get invoices for the authenticated agent, optionally filtered by date range | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | date_start | string | no | Start timestamp for date range filter (optional) | | | date_end | string | no | End timestamp for date range filter (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/invoicesget.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### companyuserdata_myuserdata `POST https://api.datalyse.io/api/1.0/companyuserdata/myuserdata.json` Get the full authenticated agent/user record from the clients collection | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/companyuserdata/myuserdata.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ## Costexplorer ### costexplorer_get `POST https://api.datalyse.io/api/1.0/costexplorer/get.json` Get aggregated cost/usage data from the calls_leads collection grouped by configurable dimensions with a date range filter. By default returns only real communication records (answered/received calls, SMS, WhatsApp) grouped by agent_id and type — excludes AI, file, payment, credit and other internal service types unless explicitly overridden via service_type. Date range must be within a single calendar year. Only master/admin users can call this endpoint. Use this to answer questions like 'cost of calls per agent this month', 'total SMS cost by country', or 'which agent spent most credits on callouts'. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | datestart_unix | number | yes | Start date as UNIX timestamp in seconds UTC (inclusive). Example: 1767225600 corresponds to 2026-01-01 00:00 UTC. | | | dateend_unix | number | yes | End date as UNIX timestamp in seconds UTC (exclusive). Example: 1769904000 corresponds to 2026-02-01 00:00 UTC. | | | group_by | array | yes | Array of dimensions to group by. Allowed values: type, country, agent_id, month, day, hour, phone, destination. Default when omitted: ["agent_id","type"] — aggregates cost per agent and per service type. | ["agent_id","type"] | | service_type | array | no | Filter by call types (optional). Default when omitted: ["callout","callin","callpush","callai","sms","smsin","whatsapp"] — aggregates received/answered calls (incl. AI voice), SMS and WhatsApp (excludes ai, aitts, payment, credit, file, etc). Allowed values in DB: callout, callin, callpush (AI push/vozpush call), callai (AI voice call), sms, smsin, whatsapp, ai, aitts, payment, credit, file, calendar, sale, ticket, emailin, emailout, emailreaded, form_completed, budget-status, fbleads, cli2cal, voiceline (monthly subscription fee of each phone line, one record per line and month). Note: callpush and callai are distinct AI voice service types, each grouped and labeled separately (not merged into 'ai'). | ["callout","callin","callpush","callai","sms","smsin","whatsapp"] | | var_agents_id | array | no | Filter by agent IDs (optional, admin/master only). Admin users are limited to agents in their department. | | | collapse_by | array | no | Re-aggregate results by these dimensions after initial grouping (optional). Subset of group_by dimensions. | | | timezone | string | no | Timezone for date range validation (optional, example: Europe/Madrid). Used to ensure the date range stays within a single calendar year in the user's local timezone. Defaults to UTC if omitted or invalid. | Europe/Madrid | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/costexplorer/get.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","datestart_unix":"","dateend_unix":"","group_by":"[\"agent_id\",\"type\"]","service_type":"[\"callout\",\"callin\",\"callpush\",\"callai\",\"sms\",\"smsin\",\"whatsapp\"]","timezone":"Europe/Madrid"}' ``` ### costexplorer_getlistcallsleads `POST https://api.datalyse.io/api/1.0/costexplorer/getlistcallsleads.json` Get call records grouped by agent and country with cost calculated from per-credit tariff. Dates in UNIX UTC seconds | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | datestart_unix | number | yes | Start date as UNIX timestamp UTC seconds (inclusive) | | | dateend_unix | number | yes | End date as UNIX timestamp UTC seconds (inclusive) | | | service_type | array | no | Filter by call types array e.g. ['callout','callin','sms'] (optional, returns all types if empty). callpush (AI push/vozpush call) and callai (AI voice call) are distinct service types. | | | var_agentfilterpayments | string | no | Filter by single agent ID (optional) | | | var_agents_id | array | no | Filter by multiple agent IDs (optional) | | | timezone | string | no | Timezone for date filtering (optional, example: Europe/Madrid) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/costexplorer/getlistcallsleads.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","datestart_unix":"","dateend_unix":""}' ``` ### costexplorer_calls `POST https://api.datalyse.io/api/1.0/costexplorer/calls.json` Call report: returns a paginated, redacted list of communication records (inbound/outbound calls, AI voice calls, SMS and WhatsApp) for a date range of at most 93 days, plus aggregate stats. Agent RBAC is applied automatically. Recordings, provider internals and non-communication service records are never returned. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | datestart_unix | number | yes | Required start date as UNIX timestamp UTC seconds (inclusive). Together with dateend_unix it must define a valid range of at most 93 days. | 1767225600 | | dateend_unix | number | yes | Required end date as UNIX timestamp UTC seconds (inclusive), greater than datestart_unix and at most 93 days later. | 1769817599 | | service_type | array | no | Filter by communication type (optional). Allowed values only: callout, callin, callpush, callai, sms, smsin, whatsapp. Defaults to all seven. Non-communication records are rejected. | callout,callin,callpush,callai,sms,smsin,whatsapp | | status | string | no | Filter by call status (optional). Common values: ANSWER (answered), NO ANSWER, BUSY, FAILED, CANCEL | | | phone | string | no | Filter by phone number substring (optional, case insensitive) | | | var_agents_id | array | no | Filter by agent IDs (optional). Ignored for agent-type users (they only see their own calls). Admin users are limited to agents in their department. | | | page | number | no | Page number, starts at 1 (optional, default 1) | 1 | | pageSize | number | no | Records per page, between 10 and 100 (optional, default 50) | 50 | | timezone | string | no | Timezone for date filtering context (optional, example: Europe/Madrid). | Europe/Madrid | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/costexplorer/calls.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","datestart_unix":1767225600,"dateend_unix":1769817599,"service_type":["callout","callin","callpush","callai","sms","smsin","whatsapp"],"page":1,"pageSize":50,"timezone":"Europe/Madrid"}' ``` ## Domains ### domain_check_availability `GET https://api.datalyse.io/api/1.0/domains/check.json` Check domain availability and pricing. Returns availability status, price in credits, original price, current balance and whether the user can afford it. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to check (e.g. example.com) | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/domains/check.json?token=YOUR_APIKEY&domain=' ``` ### domain_register `POST https://api.datalyse.io/api/1.0/domains/register.json` Buy a domain in Cloudflare and leave it fully working in the same call. Registrant contact is NOT required (Cloudflare uses the account's default registrant, already verified, so no ICANN verification email is ever sent). Credits are charged atomically BEFORE the purchase and refunded only if the registrar confirms it failed. The domain is saved with status 'active': there is no activation step and no email to confirm. The services listed in 'services' are activated right away (DNS records in Cloudflare + mail server). | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to register | | | services | array | yes | Service keys to activate on purchase. Also accepts a comma-separated string ('web,mail,cabinet') or the ?services= query param. Aliases: app=crm, cloud=infinitycloud, trade=cabinet, correo=mail, hosting=web. Default: ["mail","web"] | mail,web | | years | integer | yes | Ignored: Cloudflare always registers for 1 year | 1 | | contact | object | no | OPTIONAL registrant data (firstName, lastName, address1, city, state, postalCode, country, phone, email, org). Only stored in mail_domains.contact — it is never sent to the registrar. | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/register.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":"","services":["mail","web"],"years":"1"}' ``` ### domain_activate `POST https://api.datalyse.io/api/1.0/domains/activate.json` Re-activate / repair a domain. Since 2026-07-29 register.json already leaves the domain active and serving, so this is NOT a required step of the purchase any more: it re-creates the DNS records in Cloudflare, re-registers the domain in the mail server and marks it active. Idempotent — safe to call as many times as needed. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to re-activate | | | services | array | no | OPTIONAL service keys. Defaults to the services the domain already has (or mail+web). | mail,web | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/activate.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":"","services":["mail","web"]}' ``` ### domain_get_services `GET https://api.datalyse.io/api/1.0/domains/servers.json` Get the status of all configured services for a domain. Returns each service key, status, host and value. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to query | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/domains/servers.json?token=YOUR_APIKEY&domain=' ``` ### domain_get_managed `GET https://api.datalyse.io/api/1.0/domains/managed.json` Get all domains managed by the user's company. Returns domains filtered by company_id with status, verification state, SMTP/IMAP config and services. Does not include shared platform domains. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl 'https://api.datalyse.io/api/1.0/domains/managed.json?token=YOUR_APIKEY' ``` ### domain_add_custom `POST https://api.datalyse.io/api/1.0/domains/add.json` Add a custom (already owned) domain for verification. Saves it with status pending_verification and returns the required DNS records. The user must configure DNS and call dns/verify.json before activation. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to add | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/add.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":""}' ``` ### domain_dns_verify `GET https://api.datalyse.io/api/1.0/domains/dns/verify.json` Verify DNS configuration for a domain. Performs real DNS lookups checking MX, SPF and DMARC records. If all pass, registers the domain in the mail server and marks it as active and verified. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to verify | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/domains/dns/verify.json?token=YOUR_APIKEY&domain=' ``` ### domain_delete `POST https://api.datalyse.io/api/1.0/domains/delete_domain.json` Delete a domain completely. Removes all email accounts associated with that domain from the mail server, disconnects all agents using emails on that domain, removes all reverse proxy routes (domain, www, service subdomains), and deletes the domain record from the database. System domains cannot be deleted. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to delete | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/delete_domain.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":""}' ``` ### domain_renew `POST https://api.datalyse.io/api/1.0/domains/renewdomain.json` Check the renewal status of a domain. Manual renewal is NOT supported: domains bought in Cloudflare renew themselves via auto_renew (enabled at purchase). A nightly cron charges the client's credits one day before expiry and pushes the expiry date forward; if there is not enough balance it turns auto_renew off and deactivates the domain. Master only. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/renewdomain.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":""}' ``` ### domain_mark_delete `POST https://api.datalyse.io/api/1.0/domains/markasdelete.json` Mark a domain for deletion. Changes the domain status to 'pending_delete' so it will not be automatically renewed when it expires. The domain is not deleted immediately — it remains active until expiration. System domains cannot be marked. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name to mark for deletion | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/markasdelete.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":""}' ``` ### domain_activate_services `POST https://api.datalyse.io/api/1.0/domains/activate_services.json` Activate several services for a domain in a single call. For registered domains it creates every DNS record in one batch against Cloudflare; services flagged as proxied go through Cloudflare's orange-cloud proxy, which terminates HTTPS and talks HTTP to the origin. For external domains it verifies the public DNS before marking each service active. Same engine register.json uses. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | domain | string | yes | Domain name | | | services | array | yes | Service keys to activate: mail, web, crm, meets, infinitycloud, cabinet. Also accepts a comma-separated string and aliases (app=crm, cloud=infinitycloud, trade=cabinet). | crm,meets,mail | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/domains/activate_services.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","domain":"","services":["crm","meets","mail"]}' ``` ## Emails ### email_unsubscribe `GET https://api.datalyse.io/api/1.0/emails/unsubscribe.json` Unsubscribe an email address | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | email | string | yes | Email address to unsubscribe | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/emails/unsubscribe.json?email=' ``` ### email_send `POST https://api.datalyse.io/api/1.0/emails/send.json` Send an email | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | to | string | yes | Recipient email address | | | subject | string | yes | Email subject | Hello | | html | string | yes | Email body in HTML |

Hello world

| | cc | string | no | CC recipients (optional) | | | bcc | string | no | BCC recipients (optional) | | | lead_id | string | no | Associate email with a contact ID (optional) | | | opportunity_id | string | no | Associate email with an opportunity ID (optional) | | | signature_id | string | no | Append this signature to the email (optional, get from signatures/get) | | | tracking | string | no | Enable open tracking, set to "y" (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/send.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","to":"","subject":"Hello","html":"

Hello world

"}' ``` ### email_get_inbox `POST https://api.datalyse.io/api/1.0/emails/get.json` Get emails from inbox or sent folder | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | type | string | yes | Folder: "inbox" (default) or "sent" | inbox | | var_resultspage | string | yes | Maximum number of results | 20 | | var_page | string | yes | Page number | 1 | | search_value | string | no | Search in subject (optional) | | | unread_only | string | no | Show only unread, set to "y" (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/get.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","type":"inbox","var_resultspage":"20","var_page":"1"}' ``` ### email_get_one `POST https://api.datalyse.io/api/1.0/emails/getone.json` Get a single email with full body | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | email_id | string | yes | ID of the email | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/getone.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","email_id":""}' ``` ### email_get_signatures `POST https://api.datalyse.io/api/1.0/emails/signatures/get.json` Get all email signatures | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/signatures/get.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### email_save_signature `POST https://api.datalyse.io/api/1.0/emails/signatures/save.json` Create or update an email signature | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | signature_id | string | yes | ID of signature to update (omit to create new) | | | name | string | yes | Signature name | My Signature | | html | string | yes | Signature HTML content |

Best regards

| | is_default | string | yes | Set as default signature, "true" or "false" | false | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/signatures/save.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","signature_id":"","name":"My Signature","html":"

Best regards

","is_default":"false"}' ``` ### email_delete_signature `POST https://api.datalyse.io/api/1.0/emails/signatures/delete.json` Delete an email signature | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | signature_id | string | yes | ID of the signature to delete | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/signatures/delete.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","signature_id":""}' ``` ### email_get_templates `POST https://api.datalyse.io/api/1.0/emails/templates/get.json` Get all email templates | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/templates/get.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### email_create_template `POST https://api.datalyse.io/api/1.0/emails/templates/create.json` Create a new email template | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | name | string | yes | Template name | My Template | | subject | string | no | Default email subject (optional) | | | html | string | yes | Template HTML content |

Hello {{name}}

| Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/templates/create.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","name":"My Template","html":"

Hello {{name}}

"}' ``` ### email_edit_template `POST https://api.datalyse.io/api/1.0/emails/templates/edit.json` Edit an email template | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | template_id | string | yes | ID of the template to edit | | | name | string | no | Template name (optional) | | | subject | string | no | Default email subject (optional) | | | html | string | no | Template HTML content (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/templates/edit.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","template_id":""}' ``` ### email_delete_template `POST https://api.datalyse.io/api/1.0/emails/templates/delete.json` Delete an email template | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | template_id | string | yes | ID of the template to delete | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emails/templates/delete.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","template_id":""}' ``` ## Emailservices ### email_list_domains `GET https://api.datalyse.io/api/1.0/emailservices/domains.json` List the domains available to create a new email account / mailbox for the authenticated agent (use this before email_create_account to know which domains the user can create an email on). The result is the intersection of emelyse.com plus active mail_domains owned by the token company with domains reported active by the mail provider. SMTP/IMAP settings are server-authoritative. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token for the agent whose mailbox will be configured. Scoped app tokens require the emailservices scope. The company and agent are derived only from this token. | YOUR_APIKEY | Example: ```bash curl 'https://api.datalyse.io/api/1.0/emailservices/domains.json?token=YOUR_APIKEY' ``` Errors: - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| token_type_not_allowed \| token_company_mismatch`: The token is missing, invalid, disabled or not bound to the authenticated agent company. - HTTP 403 — `scope_denied \| permission_denied`: The app token lacks the emailservices scope or the authenticated account is not allowed. - HTTP 502 — `mail_provider_unavailable \| mail_provider_rejected \| mail_provider_invalid_response`: The bounded provider request failed or returned an unexpected status/JSON shape. - HTTP 503 — `mail_provider_not_configured`: MAIL_API_URL, MAIL_ADMIN_EMAIL and MAIL_ADMIN_PASSWORD are not all configured in the server environment. - HTTP 504 — `mail_provider_timeout`: The mail provider did not answer before the configured timeout. - HTTP 500 — `internal_error`: An unexpected tenant-domain or configuration mapping read failed. Notes: - The endpoint never accepts company_id or agent_id. Tenant and agent identity come exclusively from apibridge_authorize and the token. - A Mongo mail_domains row is not sufficient: every returned domain is rechecked as active against the mail provider. - Responses use Cache-Control: no-store and expose no provider credentials or mailbox passwords. ### email_create_account `POST https://api.datalyse.io/api/1.0/emailservices/create.json` Create a new email account / mailbox on a managed domain and configure it for the agent authenticated by token (this is the tool to use when the user asks to create an email, create a mailbox, or set up a new email address like name@yourdomain.com). The shared socket/HTTP core validates tenant domain ownership and live provider state, creates and verifies the new mailbox before an atomic local switch, then disconnects the old poller and removes only a strictly owned previous managed mailbox. HTTP calls require an idempotency key. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token for the agent being configured. Scoped app tokens require the emailservices scope. company_id and agent_id are always derived from this token. | YOUR_APIKEY | | email | string | yes | Full ASCII mailbox address. The local part is 1-64 characters, starts with an alphanumeric character and permits alphanumerics, dot, underscore, plus and hyphen; consecutive/trailing dots are rejected. Domain must be emelyse.com or an active mail_domains row owned exactly by the token company. | support@emelyse.com | | password | string | no | Optional mailbox password. If omitted or empty, the server generates one with crypto.randomBytes. Supplied values must be 12-128 visible ASCII characters and contain lowercase, uppercase, digit and symbol. | | | idempotency_key | string | yes | Required HTTP idempotency key, 8-128 characters from A-Z, a-z, 0-9, dot, underscore, colon or hyphen. The same key and payload replays without creating another mailbox; the same key with a different email/password returns idempotency_conflict. | mail-create-20260726-001 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/emailservices/create.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","email":"support@emelyse.com","idempotency_key":"mail-create-20260726-001"}' ``` Generate a strong password server-side: ```json { "token": "YOUR_APIKEY", "email": "support@emelyse.com", "idempotency_key": "mail-create-20260726-001" } ``` Use a caller-supplied strong password: ```json { "token": "YOUR_APIKEY", "email": "sales@owned.example", "password": "ExampleStrongPass123!", "idempotency_key": "mail-create-20260726-002" } ``` Errors: - HTTP 400 — `invalid_request \| email_required \| invalid_email \| invalid_local_part \| invalid_password \| idempotency_key_required \| invalid_idempotency_key \| conflicting_idempotency_key \| agent_id_not_allowed \| company_id_not_allowed`: The request shape, address, password or HTTP idempotency key is invalid. Raw agent_id and company_id overrides are rejected before authentication normalization. - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| token_type_not_allowed \| token_company_mismatch`: The API token is missing, invalid, disabled or bound to a different tenant. - HTTP 403 — `scope_denied \| permission_denied \| domain_not_allowed`: The token lacks emailservices scope/permission or the requested domain is not active and owned exactly by the token company. - HTTP 404 — `agent_not_found`: The authenticated agent no longer exists in the token company. - HTTP 409 — `idempotency_conflict \| operation_in_progress \| recovery_manual_required \| domain_not_active \| email_in_use \| configuration_changed`: The idempotency key has a different payload, another live lease holds the company+agent lock, uncertain stale provider state requires manual reconciliation, the provider domain is inactive, the mailbox already exists, or local mail state changed concurrently. - HTTP 500 — `persistence_failed \| internal_error`: The local atomic switch failed. A mailbox created by this operation is compensated only after strict provider id/email/company/agent verification. - HTTP 502 — `mail_provider_unavailable \| mail_provider_rejected \| mail_provider_invalid_response \| mail_provider_verification_failed`: The provider request failed, returned an unexpected bounded JSON/status, or the newly created mailbox could not be verified. - HTTP 503 — `mail_provider_not_configured \| safety_unavailable`: Required provider environment credentials are absent or the unique lock/idempotency indexes are unavailable. - HTTP 504 — `mail_provider_timeout`: A mail provider operation exceeded its configured timeout. Notes: - HTTP 201 means the mailbox was created and committed. HTTP 200 means an idempotent replay of the same completed job. A concurrent in-progress job returns HTTP 409 plus Retry-After. - SENSITIVE RESPONSE: password is the only returned secret. Store it securely and never cache or log the response. The API sends Cache-Control: no-store, private and Pragma: no-cache. - On replay, password is read only from the same authenticated agent when emailservices_job_id and request hash still match this exact job. If the agent later changes configuration, password is null and password_available is false. - The new provider mailbox is created and re-read with exact company_id and agent_id before any local/OAuth/poller/old-mailbox change. Mongo transaction failure leaves old local/OAuth state intact and triggers deletion only of the verified mailbox created by this job. - OAuth connections and clients_azure are removed inside the local transaction. Poller disconnection and strictly-owned previous managed-mailbox deletion happen only after commit; pending cleanup is retried by an exact idempotent replay. - The operation is serialized by a unique active company_id+agent_id lock plus a 60-300 second fenced lease (180 seconds by default). A stale claimed job can be taken over by the same idempotency key. - A stale job with an exact provider marker is resumed only after matching provider user id, domain id, email, company_id and agent_id. If a generated password was lost in a crash, that exact marked mailbox is compensated and recreated; ambiguous pre-marker state returns recovery_manual_required and releases the active lock. - Terminal idempotency jobs expire through a TTL index after 24 hours. Passwords are never stored in job documents or logs. - MAIL_API_URL, MAIL_ADMIN_EMAIL and MAIL_ADMIN_PASSWORD must be supplied by the server environment; the shared core has no credential fallback. ## Entities ### entities_get_schemas `POST https://api.datalyse.io/api/1.0/entities/schemas.json` Discover ALL CRM entities and their complete schemas in one call. This is the mandatory entry point for AI assistants and MCP integrations before reading or writing records. The built-in record entities (leads/contacts, companies, opportunities, tasks, tickets, and — when the deployment enables finance entities — budgets and payments) and every custom entity (cust_...) expose the SAME record contract: properties contains the exact identifiers accepted inside values; direct associations are writable properties of type 'association'; statusgroups tells which value to send for each status field; and record_api contains the same generic endpoints to list, get, create, edit, delete, read activity and add notes. Status value rule: mode='index' -> send legacyIndex, mode='id' -> send id, mode='value' -> send id/value, always under values[statusgroups[].docField]. Properties with read_only=true must not be written. Budgets and payments: check record_api.supported per entity — when true they use the same generic record endpoints (budgets add a 'lineitems' property 'concepts' whose value is an array of row objects following properties.concepts.lineitems.columns, and payments amounts are declared stored_as='string' but always read and written as numbers here); when false they keep their document-specific endpoints listed in record_api. Budgets id_lead/idsale are set at creation and immutable afterwards; payments create is also available via the specific endpoint /api/1.0/payments/create.json (contract unchanged). GET is also supported with token in the query string. EVERY property always includes required:'y'\|'n' and unique:'y'\|'n' for both system and custom entities; omitted internal flags are normalized to 'n'. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/schemas.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` Get all entities and schemas: ```json { "token": "YOUR_APIKEY" } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `company_not_found`: The company linked to the token no longer exists - `server_error`: Unexpected server error Notes: - Call this endpoint first and cache its result only for the current company/token. Never guess entity keys, property identifiers, status values or association fields. - UNIFORM RECORD API: leads, companies, opportunities, tasks, tickets and cust_* all use record_api.list/get/create/edit/delete/activity/add_note. The old module-specific endpoints remain compatible, but MCP/AI should prefer record_api. - STATUS VALUES: use the group's docField as the field name inside values. mode='index' uses legacyIndex; mode='id' uses id; mode='value' uses the status id/value. The first status flagged isdefault='y' is used when creation omits it. - ASSOCIATIONS: direct links are also exposed as properties with type='association'. Send the target record's 24-character id as values[association.identifier]. Associations with inverse='y' are calculated collections and cannot be written. - read_only=true means the property can be returned and filtered but must not be sent to create/edit. Dates use unix UTC epoch seconds; accepted date strings are normalized server-side where documented. - Budgets are intentionally outside the uniform record contract; follow their specific hints instead of calling /entities record operations. - PROPERTY CONSTRAINT FLAGS: every properties entry always returns required ('y' or 'n') and unique ('y' or 'n'), including system, status, assignment and association properties. required='y' means a value is mandatory on creation; unique='y' means no other record of that entity may use the same non-empty value. ### entities_get_records `POST https://api.datalyse.io/api/1.0/entities/getrecords.json` Search and list records through one POST endpoint for contacts (entitykey 'leads'), companies, opportunities, tasks, tickets and every custom entity (cust_...). Call schemas.json first, then use its property identifiers in filters/orderby. Filters support eq, ne, in, nin, gt, gte, lt, lte and re, including several operators on one field for ranges. The response has one stable shape for system and custom entities: clean records with id, schema properties, assignment fields, epoch-second dates, resolved _label fields and assoclabels where available. Visibility is always enforced from the token. Budgets use their specific API. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | filters | object or JSON string | no | Filters on property identifiers (from schemas.json). Simple form {"identifier": "value"} means equals; an array value means IN; advanced form {"identifier": {"op": "gte", "value": 100}} with ops: eq, ne, in, nin, gt, gte, lt, lte, re (regex contains, case-sensitive; only meaningful on text-like properties — on 'number' properties it never matches anything, and on opportunities 'expecteddate' it is rejected with invalid_filter_op_re_for_expecteddate). Status fields: send the legacyIndex for contacts/companies (statusgroups mode 'index') or the status 'id' for custom entities (mode 'id'). Association properties: send the _id string of the target record. agent_id and agents_id support only eq/in (agents_id is an array: eq = contains, in = contains any). Also accepts a JSON-encoded string. (optional) RANGE/multi-op form: a value object WITHOUT 'op' whose keys are operators applies them all to the identifier — {"identifier": {"gte": 100, "lte": 200}} (both bounds merged with AND); works for any property type and is THE way to express from-to ranges in a single call. | [object Object] | | search | string | no | Free-text search over the entity's text properties (name, email, phone...). Case-insensitive. (optional) | | | page | string | no | Page number, starts at 1. (optional) | 1 | | perpage | string | no | Results per page, max 100. (optional) | 20 | | orderby | string | no | Property identifier to sort by. Defaults to creation date, newest first. (optional) | creation_date | | orderdir | string | no | Sort direction: 'asc' or 'desc'. Defaults to 'desc'. (optional) | desc | | count | string | no | Set to 'y' to also get the exact total count in 'total' (slower). By default 'total' is null and pagination relies on 'hasmore'. (optional) | | | datefrom | string | no | Start of creation-date range, format YYYY-MM-DD. Requires dateto and timezone. (optional) | 2026-01-01 | | dateto | string | no | End of creation-date range, format YYYY-MM-DD. Requires datefrom and timezone. (optional) | 2026-12-31 | | timezone | string | no | IANA timezone for the date range, e.g. Europe/Madrid. Required together with datefrom and dateto. (optional) | Europe/Madrid | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/getrecords.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"leads","filters":{"status":"0"},"perpage":20}' ``` Contacts with status New (legacyIndex 0): ```json { "token": "YOUR_APIKEY", "entitykey": "leads", "filters": { "status": "0" }, "perpage": 20 } ``` Spanish contacts created in 2026, newest first: ```json { "token": "YOUR_APIKEY", "entitykey": "leads", "filters": { "country": "es" }, "datefrom": "2026-01-01", "dateto": "2026-12-31", "timezone": "Europe/Madrid", "orderby": "creation_date", "orderdir": "desc" } ``` Custom entity: price >= 100000 AND status Available (by id), with exact total: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "filters": { "ab12price8k2j3h4g5": { "op": "gte", "value": 100000 }, "status_main": "st_1a2b3c" }, "count": "y" } ``` Free-text search in companies: ```json { "token": "YOUR_APIKEY", "entitykey": "companies", "search": "acme" } ``` Records linked to a contact (association property = contact _id): ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "filters": { "ab12owner9q8w7e6r5": "69d9d868740030527e46055a" } } ``` Tasks between two dates (range on a datetime field): ```json { "token": "YOUR_APIKEY", "entitykey": "tasks", "filters": { "datestartunixutc": { "gte": "2026-01-01", "lte": "2026-02-01 23:59" } }, "orderby": "datestartunixutc", "orderdir": "asc" } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `empty_entitykey`: Missing entitykey parameter - `invalid_filters`: filters is not an object - `entity_not_found`: No entity with that key in this company (check /api/1.0/entities/schemas.json) - `invalid_filter_identifier_`: A filter key is not a property of the entity; the response includes 'valid_identifiers' with every filterable identifier - `invalid_filter_op_`: Unsupported filter operator (allowed: eq, ne, in, nin, gt, gte, lt, lte, re) - `invalid_filter_op_re_for_expecteddate`: Op 're' (regex) was used on the opportunities 'expecteddate' filter; regex never matches a numeric date field — use eq/ne/in/nin/gt/gte/lt/lte instead - `invalid_filter_value_`: The filter value is missing or cannot be cast to the property type (e.g. non-numeric value on a number property) - `use_specific_endpoint`: Budgets are queried through their own endpoint; the 'use_endpoint' field of the response tells you which one - `server_error`: Unexpected server error Notes: - Records come CLEAN: only fields defined in the entity schema plus id/agent_id/agents_id/creation_date/last_update (and a raw 'date' field on entities that store it, e.g. opportunities — epoch backing field of expecteddate — and tasks). Status fields are also resolved to human labels in extra '_label' fields (e.g. status '0' + status_label 'New'). For custom entities, 'assoclabels' maps each association property to the display label of the linked record. - Pagination: by default 'total' is null and 'hasmore' tells you if there is a next page (cheap). Send count='y' only when you really need the exact total. - creation_date and last_update are unix UTC epoch SECONDS (never milliseconds) — like EVERY date field of the API. Status filter values: legacyIndex for contacts/companies, status 'id' for custom entities (check statusgroups[].mode in schemas.json). - Visibility is enforced server-side with the permissions of the user linked to the token: agents only see their records; admins see their department on built-in entities and everything on custom entities. Filtering by agent_id supports only eq/in. - Call /api/1.0/entities/schemas.json first to discover entity keys and property identifiers. - Each record's 'id' is the record_id used by the same generic endpoints for every supported entity: getrecord.json, editrecord.json, deleterecord.json, getactivity.json and addnote.json. - Opportunities: 'expecteddate' (expected close date) works both in 'filters' and 'orderby'. As filter value send epoch UTC SECONDS (a millisecond epoch is auto-normalized) or a 'yyyy-mm-dd' date string (parsed like the write endpoints, at 00:00); anything else returns invalid_filter_value_expecteddate instead of matching nothing. For op 'in'/'nin' send an array (or a comma-separated string). Op 're' is not supported on this field. Internally it maps to the physical 'date' field. Example filter: {"expecteddate": {"op": "lte", "value": 1790000000}}. - DATE RANGES: 'datetime' (and legacy 'date') properties store unix UTC epoch seconds — on CUSTOM entities and on BUILT-IN ones alike (e.g. tasks 'datestartunixutc' / 'dateendunixutc'). Filter from-date-to-date with the RANGE form (several ops in one value object): filters = {"datestartunixutc": {"gte": 1790000000, "lte": 1795000000}}. Values accept epoch seconds (ms auto-normalized) or 'yyyy-mm-dd' / 'yyyy-mm-dd hh:mm' strings (converted server-side), so {"gte": "2026-01-01", "lte": "2026-02-01"} also works. Op 're' is rejected on date fields. - AGENT FILTERS: 'agent_id' is the MAIN assigned employee (single id; also accepts 'unassigned') and 'agents_id' is the ARRAY of ALL assigned employees. On 'agents_id', op 'eq' matches records whose array CONTAINS that agent and op 'in' matches records containing ANY of the listed agents — e.g. filters = {"agents_id": {"op": "in", "value": ["64a1...", "64b2..."]}}. Both fields allow ONLY eq/in; users of type 'agent' cannot filter by agent fields, and admins only within the agents they manage. Works identically on custom and built-in entities. - SYSTEM DATES: every entity exposes 'datesales' (creation date) and 'last_updatesales' (last update) — virtual identifiers over the physical creation_date/last_update fields, BOTH stored as unix UTC epoch in SECONDS. Filter them by range like any date: filters = {"last_updatesales": {"gte": "2026-07-01", "lte": "2026-07-21 23:59"}} (epoch seconds also accepted). Ops eq/ne/gt/gte/lt/lte; 're' is rejected. Works on custom and built-in entities. - This list operation has the same request and response contract for leads, companies, opportunities, tasks, tickets and cust_*; do not switch to module-specific list endpoints. - To fetch exactly one known id use /api/1.0/entities/getrecord.json instead of a broad search. ### entities_create_record `POST https://api.datalyse.io/api/1.0/entities/createrecord.json` Create ONE record with the SAME POST contract in contacts (entitykey 'leads'), companies, opportunities, tasks, tickets or any custom entity (cust_...). First call schemas.json. Put fields inside values using properties[].identifier exactly; this includes status fields and direct association fields. STATUS RULE: inspect statusgroups[].mode and send legacyIndex for mode='index', id for mode='id', or id/value for mode='value', under values[statusgroups[].docField]. ASSOCIATIONS: send the target record id under the association property's identifier. DATES: send unix UTC epoch seconds (preferred) or an accepted date string. Assignment can be sent as values.agent_id; agents cannot create unassigned records and admins remain department-scoped. The legacy top-level agent_id/status parameters are accepted only for backward compatibility. Returns the same {status, entitykey, record_id} shape for every supported entity. Budgets are not supported here. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | values | object or JSON string | yes | Fields for the new record, keyed ONLY by property identifiers from schemas.json. Include every required property. Status fields use their statusgroup docField and mode; association fields take the target record id; agent_id optionally assigns the record. Also accepts a JSON-encoded string. Required. | [object Object] | | agent_id | string | no | Deprecated compatibility alias for values.agent_id. Prefer values.agent_id. 'unassigned' is allowed only when the token's role permits it. (optional) | | | status | string | no | Deprecated compatibility alias for contacts/companies only. Prefer values[statusgroups[].docField] for every entity. (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/createrecord.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"leads","values":{"name":"Marc","lastname":"Doe","email":"example@exampleemail.com","phone":"34682288888","country":"es","status":"0"}}' ``` Create a contact: ```json { "token": "YOUR_APIKEY", "entitykey": "leads", "values": { "name": "Marc", "lastname": "Doe", "email": "example@exampleemail.com", "phone": "34682288888", "country": "es", "status": "0" } } ``` Create an opportunity linked to a contact: ```json { "token": "YOUR_APIKEY", "entitykey": "opportunities", "values": { "description": "Enterprise renewal", "total": 25000, "currency": "eur", "expecteddate": 1790000000, "status": "0", "statusd": "in progress", "id_lead": "69d9d868740030527e46055a" } } ``` Create a task linked to a contact: ```json { "token": "YOUR_APIKEY", "entitykey": "tasks", "values": { "subject": "Follow-up call", "text": "Review the proposal", "datestartunixutc": 1790000000, "dateendunixutc": 1790003600, "priority": "1", "status": "0", "id_lead": "69d9d868740030527e46055a" } } ``` Create a ticket linked to a contact: ```json { "token": "YOUR_APIKEY", "entitykey": "tickets", "values": { "ticketname": "Login issue", "ticketdescription": "Customer cannot sign in", "status": "0", "id_lead": "69d9d868740030527e46055a" } } ``` Create a custom record with status and association: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "values": { "ab12name7f3k2m9q1z": "Villa Sunrise", "ab12price8k2j3h4g5": 250000, "status_main": "st_1a2b3c", "ab12owner9q8w7e6r5": "69d9d868740030527e46055a" } } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `empty_entitykey`: Missing entitykey - `empty_values`: values is missing or is not an object - `entity_not_found`: The entity key does not exist in this company - `entity_not_supported`: The entity exists but is outside the uniform record API (budgets) - `permission_denied`: The token user cannot create/assign the record with the requested scope - `required property is empty`: A required custom-entity property is missing - `invalid option for property `: A select value is not one of the options returned by schemas.json - `invalid date for property `: A datetime value cannot be converted to unix UTC epoch seconds - `create_failed`: The underlying entity creator did not return a record id - `server_error`: Unexpected server error - `invalid_property_identifier_`: values contains a field that is not a writable identifier from schemas.json; valid_identifiers is returned - `read_only_property_`: values contains a schema property marked read_only=true - `associated_record_not_visible_`: The target association record exists but is outside the token user visibility Notes: - One endpoint creates records in all supported system entities (finance entities included when enabled) and custom entities. Do not use a module-specific create endpoint when record_api.create is present. - Field names always come from schemas.json. For a newly created custom entity, createentity.json already returns created.properties identifiers, so a second schema call is optional. - For opportunities, use identifier total (not the legacy amount parameter) and expecteddate as epoch seconds; the adapter maps these to legacy storage without changing the public contract. - For tasks, datestartunixutc/dateendunixutc are canonical and the adapter maintains legacy calendar date/time fields for frontend and cron compatibility. - The old top-level status and agent_id inputs remain accepted so existing integrations are not broken; new MCP/AI calls should place both inside values. - The operation uses each module's existing creator, so workflows, duplicate checks, history/feed and role visibility continue to run. - Unknown and read-only identifiers are rejected before any write; internal legacy fields cannot be injected through values. ### entities_edit_record `POST https://api.datalyse.io/api/1.0/entities/editrecord.json` Partially update ONE existing record through the same endpoint for leads, companies, opportunities, tasks, tickets and custom entities (cust_...). Call schemas.json first and send only changed fields inside values, keyed by schema property identifiers. Statuses, associations, assignment and dates follow exactly the same rules as createrecord.json. Unsent fields remain unchanged. Visibility and role/department scope are enforced before writing. The adapter preserves each system module's legacy side effects and the custom entity validator/history. Budgets are not supported here. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | record_id | string | yes | The _id of the record to edit: the 'id' field returned by getrecords.json or the 'record_id' returned by createrecord.json (24-char hex). Required. | 69d9d868740030527e46055a | | values | object or JSON string | yes | Partial object containing only fields to change, keyed by identifiers from schemas.json. Status fields use statusgroups[].docField/mode; associations use target record ids; dates use epoch seconds; agent_id/agents_id are role-scoped. Also accepts a JSON-encoded string. Required. | [object Object] | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/editrecord.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"leads","record_id":"69d9d868740030527e46055a","values":{"phone":"34600000000","status":"1"}}' ``` Edit a contact: ```json { "token": "YOUR_APIKEY", "entitykey": "leads", "record_id": "69d9d868740030527e46055a", "values": { "phone": "34600000000", "status": "1" } } ``` Edit opportunity amount, date and stage: ```json { "token": "YOUR_APIKEY", "entitykey": "opportunities", "record_id": "69d9d868740030527e46055a", "values": { "total": 30000, "expecteddate": 1791000000, "statusd": "won" } } ``` Reschedule a task: ```json { "token": "YOUR_APIKEY", "entitykey": "tasks", "record_id": "69d9d868740030527e46055a", "values": { "datestartunixutc": 1792000000, "dateendunixutc": 1792003600 } } ``` Edit a custom record and association: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "record_id": "69d9d868740030527e46055a", "values": { "ab12price8k2j3h4g5": 199000, "status_main": "st_4d5e6f", "ab12owner9q8w7e6r5": "69d9d868740030527e46055a" } } ``` Errors: - `EMPTY_TOKEN`: Missing token - `empty_entitykey`: Missing entitykey - `invalid_record_id`: record_id is not a valid 24-character id - `empty_values`: No fields were supplied - `entity_not_found`: Unknown entity key - `entity_not_supported`: Budgets use their specific API - `record_not_found`: The record does not exist, was deleted, or is outside token visibility - `permission_denied`: The token user cannot make the requested assignment/change - `save_failed`: The underlying entity engine did not confirm the update - `server_error`: Unexpected server error - `invalid_property_identifier_`: values contains an unknown/non-writable identifier; valid_identifiers is returned - `read_only_property_`: The property is marked read_only=true in schemas.json - `associated_record_not_visible_`: The target association record is outside token visibility - `invalid_agents_id`: agents_id must be an array and remains role/department scoped Notes: - The update is partial and uses one public contract; module-specific edit endpoints remain only for backward compatibility. - A visibility read is performed before every update, then system entities use entitiessales_saverecord and custom entities use the same bridge to their schema validator. - Use getrecord.json after an update when the final normalized record is needed. ### entities_get_record `POST https://api.datalyse.io/api/1.0/entities/getrecord.json` Get ONE record by id with the same clean response shape for leads, companies, opportunities, tasks, tickets and custom entities. The returned record contains id (never _id), schema properties, assignment fields, normalized epoch-second dates, status labels and association labels when available. Token visibility is enforced. Call schemas.json first. Budgets are not supported. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | record_id | string | yes | The record id returned by getrecords.json or create/editrecord.json. Required. | 69d9d868740030527e46055a | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/getrecord.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"leads","record_id":"69d9d868740030527e46055a"}' ``` Get one contact: ```json { "token": "YOUR_APIKEY", "entitykey": "leads", "record_id": "69d9d868740030527e46055a" } ``` Get one custom record: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "record_id": "69d9d868740030527e46055a" } ``` Errors: - `invalid_record_id`: Invalid id - `entity_not_found`: Unknown entity - `record_not_found`: Missing, deleted or not visible record - `entity_not_supported`: Budgets use specific APIs Notes: - Use this endpoint for a known id; use getrecords.json for search/filter/pagination. - The clean record format is shared with each item returned by getrecords.json. ### entities_delete_record `POST https://api.datalyse.io/api/1.0/entities/deleterecord.json` Delete ONE visible record using the same request for leads, companies, opportunities, tasks, tickets and custom entities. System modules retain their existing deletion and related cleanup behavior; custom records use their existing soft-delete behavior. This is destructive: an AI/MCP must call it only after the user explicitly identifies and confirms the record. Budgets are not supported. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | record_id | string | yes | Exact id of the record to delete. Required. | 69d9d868740030527e46055a | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/deleterecord.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"cust_ab12cd34ef56gh78","record_id":"69d9d868740030527e46055a"}' ``` Delete a confirmed record: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "record_id": "69d9d868740030527e46055a" } ``` Errors: - `invalid_record_id`: Invalid id - `record_not_found`: Missing/deleted/not visible record - `delete_failed`: Underlying deletion failed Notes: - Always use getrecord.json to verify the target and obtain explicit user confirmation before deletion. - Old entity-specific delete endpoints remain compatible. ### entities_get_activity `POST https://api.datalyse.io/api/1.0/entities/getactivity.json` Get the paginated activity/history feed of one visible record through a single endpoint for leads, companies, opportunities, tasks, tickets and custom entities. Results are newest first and normalized with id instead of Mongo _id. Optional type filters the event type. Company activity also includes activity of contacts linked to that company, matching existing CRM behavior. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | record_id | string | yes | Record id. Required. | 69d9d868740030527e46055a | | type | string | no | Optional exact activity type filter. (optional) | | | page | number | no | Page number starting at 1. (optional) | 1 | | perpage | number | no | Items per page, maximum 100. (optional) | 20 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/getactivity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"opportunities","record_id":"69d9d868740030527e46055a","page":1,"perpage":20}' ``` Get record activity: ```json { "token": "YOUR_APIKEY", "entitykey": "opportunities", "record_id": "69d9d868740030527e46055a", "page": 1, "perpage": 20 } ``` Errors: - `record_not_found`: Missing/deleted/not visible record - `entity_not_supported`: Budgets use specific APIs Notes: - Visibility is checked on the parent record before its feed is queried. - date values are unix UTC epoch seconds. - Pagination spans the live calls_leads collection and monthly calls_leads_YYYY_MM archives back to the record creation month. ### entities_add_note `POST https://api.datalyse.io/api/1.0/entities/addnote.json` Add a note to ONE visible record through the same endpoint for leads, companies, opportunities, tasks, tickets and custom entities. The note is attributed to the user linked to the token and appears in getactivity.json/the CRM feed. type defaults to note; delivery is the only alternative. This endpoint does not send an email, SMS, WhatsApp message or call. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. Required. | YOUR_APIKEY | | entitykey | string | yes | Entity key from schemas.json: 'leads' (contacts), 'companies', 'opportunities', 'tasks', 'tickets', or a custom key starting with 'cust_'. Required. | leads | | record_id | string | yes | Record id. Required. | 69d9d868740030527e46055a | | text | string | yes | Non-empty note text. Required. | Customer requested a follow-up next week. | | type | string | no | 'note' (default) or 'delivery'. (optional) | note | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/addnote.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"tickets","record_id":"69d9d868740030527e46055a","text":"Customer supplied the requested logs."}' ``` Add a note to a ticket: ```json { "token": "YOUR_APIKEY", "entitykey": "tickets", "record_id": "69d9d868740030527e46055a", "text": "Customer supplied the requested logs." } ``` Errors: - `empty_text`: No note text supplied - `record_not_found`: Missing/deleted/not visible record Notes: - This only writes an internal CRM note; it never communicates externally. - Read the resulting feed with getactivity.json. ### entities_create_entity `POST https://api.datalyse.io/api/1.0/entities/createentity.json` Create a NEW CUSTOM ENTITY in the CRM — a brand-new record type / custom object / custom module (e.g. 'Properties', 'Contracts', 'Invoices', 'Vehicles') — COMPLETE IN ONE SINGLE CALL: the entity itself (name, name_plural, icon, color) PLUS all its initial PROPERTIES (fields, associations included — 'properties' parameter) PLUS its initial STATUSES ('statuses' parameter — the first one becomes the default). THIS ENDPOINT IS THE TOOL FOR THAT and the RECOMMENDED way for AI assistants and integrations: if a user asks to create a custom entity ('entidad personalizada'), send EVERYTHING here in one request instead of chaining saveproperty/savestatus calls. Every property type is supported with the same format as saveproperty.json: text, tags, textarea, select (REQUIRES 'options'), phone, email, number, datetime (ALWAYS stored as unix UTC epoch in SECONDS), country, language (lowercase ISO 639-1 code; options auto-provided by the engine), currency (lowercase ISO 4217 code; options auto-provided), html (rich HTML string, sanitized server-side), association (REQUIRES 'targetEntity' = an EXISTING entitykey). The entity is also born with a required 'Name' text property. The response's 'created' object maps every property name to its generated identifier (the auto-created 'Name' property included, flagged required:'y') and every status label to its id, so createrecord.json can be called IMMEDIATELY with no schemas.json lookup — just send a value for every required:'y' property. saveproperty.json and savestatus.json remain available to add MORE fields/statuses LATER or to edit existing ones. Built-in system entities (leads, companies, opportunities, tasks, tickets, budgets) already exist and cannot be created here. The token's user must be master or admin. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. The linked user must be master or admin. Required. | YOUR_APIKEY | | name | string | yes | Singular name of the new entity. Required. | Property | | name_plural | string | no | Plural name. Defaults to the singular name. (optional) | Properties | | icon | string | no | Tabler icon class. Defaults to 'ti ti-box'. (optional) | ti ti-home | | color | string | no | Hex color for the entity. Defaults to '#2563eb'. (optional) | #16a34a | | properties | array or JSON string | no | INITIAL properties (fields) of the entity, created in the SAME call: array of {name (required), type (defaults to 'text'; one of text, tags, textarea, select, phone, email, number, datetime, country, language (lowercase ISO 639-1 code like 'es'/'en'/'pt-br'; the engine auto-provides the options, do NOT send them), currency (lowercase ISO 4217 code like 'eur'/'usd'; options auto-provided), html (rich HTML string; sanitized server-side: script/style/iframe tags, on* attributes and javascript: URLs are stripped), association), required ('y'/'n', default 'n'), unique ('y'/'n', default 'n'), options (REQUIRED for type select: array of strings or {text, textshow, selected:'y'\|'n'} objects), targetEntity (REQUIRED for type association: an EXISTING entitykey of the company — it cannot point to the entity being created), cardinality (association only, defaults to 'many-to-one')}. Max 100. Do NOT include a property named 'Name': the entity is always born with it. Every entry is validated upfront: if any is invalid NOTHING is created. More fields can be added later with /api/1.0/entities/saveproperty.json. Also accepts a JSON-encoded string. (optional) | [object Object],[object Object],[object Object],[object Object] | | statuses | array or JSON string | no | INITIAL statuses of the entity: array where each item is a label string or a {label, color} object. The FIRST one becomes the default status for new records. Max 100. Statuses can be added/edited later with /api/1.0/entities/savestatus.json. Also accepts a JSON-encoded string. (optional) | Available,[object Object],[object Object] | | statusgroup | object | no | Alternative form of 'statuses' with a custom group name: {name, statuses:[...]} (same statuses format, e.g. {"name":"Pipeline","statuses":[{"label":"Draft"},{"label":"Confirmed","color":"#22c55e"}]}). Use one or the other, not both — if both are sent, 'statusgroup' wins and 'statuses' is ignored. (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/createentity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","name":"Property","name_plural":"Properties","icon":"ti ti-home","color":"#16a34a","properties":[{"name":"Reference","type":"text","required":"y","unique":"y"},{"name":"Price","type":"number"},{"name":"Type","type":"select","options":["Flat","House","Office"]},{"name":"Signing date","type":"datetime"},{"name":"Owner","type":"association","targetEntity":"leads"}],"statuses":["Available",{"label":"Reserved","color":"#f59e0b"},{"label":"Sold","color":"#ef4444"}]}' ``` Complete entity in ONE call (recommended): properties + association + statuses: ```json { "token": "YOUR_APIKEY", "name": "Property", "name_plural": "Properties", "icon": "ti ti-home", "color": "#16a34a", "properties": [ { "name": "Reference", "type": "text", "required": "y", "unique": "y" }, { "name": "Price", "type": "number" }, { "name": "Type", "type": "select", "options": [ "Flat", "House", "Office" ] }, { "name": "Signing date", "type": "datetime" }, { "name": "Owner", "type": "association", "targetEntity": "leads" } ], "statuses": [ "Available", { "label": "Reserved", "color": "#f59e0b" }, { "label": "Sold", "color": "#ef4444" } ] } ``` Minimal entity: ```json { "token": "YOUR_APIKEY", "name": "Contract" } ``` Entity with initial statuses only (fields can be added later): ```json { "token": "YOUR_APIKEY", "name": "Excursion", "statuses": [ "Draft", { "label": "Confirmed", "color": "#22c55e" } ] } ``` Entity with a named status group: ```json { "token": "YOUR_APIKEY", "name": "Excursion", "statusgroup": { "name": "Pipeline", "statuses": [ { "label": "Draft" }, { "label": "Confirmed", "color": "#22c55e" } ] } } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `permission_denied`: The token's linked user is not master or admin - `empty_name`: Missing or blank entity name — or a 'properties' entry with a blank name (then 'failed_operation' points to the index, e.g. properties[2]) - `company_not_found`: Company of the token not found - `invalid_properties_format`: 'properties' sent but malformed: it must be a non-empty array (or JSON-encoded array) of {name, type?, required?, unique?, options?, targetEntity?, cardinality?} objects. Omit it for an entity without extra fields - `invalid_property_type_`: A property entry has an unknown type; the response includes 'allowed_types' and 'failed_operation' points to properties[i]. Note: 'date' is not creatable — use 'datetime' - `invalid_options`: A property of type select has no valid 'options': a non-empty array of strings or {text, textshow, selected:'y'\|'n'} objects is required; 'failed_operation' points to properties[i] - `empty_target_entity`: A property of type association has no 'targetEntity'; 'failed_operation' points to properties[i] - `target_entity_not_found`: The targetEntity of an association property does not exist in this company; the response includes 'valid_entitykeys'. Associations can only point to entities that ALREADY exist — never to the entity being created in this same call. Create the target entity first, or create this entity without the association and add it afterwards with saveproperty.json - `duplicate_property_name`: Two properties in the request share the same name, or a property is named 'Name' (the entity is always born with a default required 'Name' property); 'failed_operation' points to properties[i] - `invalid_statusgroup_format`: 'statuses'/'statusgroup' sent but malformed: 'statuses' must be an array of labels or {label, color} objects; 'statusgroup' must be {name?, statuses:[...]} with a non-empty array. Omit both for an entity without statuses - `empty_status_label`: A status entry has a blank label; the response 'failed_operation' points to the index (statuses[i]) - `too_many_operations`: More than 100 properties or more than 100 statuses in one call - `partial_creation`: RARE: the entity was created but a property failed mid-way. The response includes 'entitykey', 'created' with everything that WAS created and 'failed_operation' with what failed. Do NOT retry the whole createentity call (it would create a duplicate entity): finish the missing pieces with saveproperty.json - `save_failed`: Unexpected failure creating the entity (nothing was created). Raw engine errors can also come back verbatim in 'error' - `server_error`: Unexpected server error - `invalid_cardinality`: An association property cardinality is not 'many-to-one' or 'one-to-many' Notes: - TYPICAL AI FLOW to build a full custom entity: 1) createentity.json (this endpoint) with 'properties' AND 'statuses' in the SAME request -> the response 'created' maps every property name to its generated identifier (the default 'Name' included) and every status label to its 'st_...' id. 2) createrecord.json immediately, using those identifiers as field names in 'values' — send a value for EVERY entry with required:'y' ('Name' always is) and the status id in values.status_main — no schemas.json call needed. 3) getrecords.json / editrecord.json to work with records. saveproperty.json / savestatus.json are ONLY needed to add more fields/statuses LATER or to edit existing ones. - VALIDATION is strict and upfront: every entry of 'properties' and 'statuses' is validated BEFORE anything is written — if any entry is invalid NOTHING is created and the error's 'failed_operation' points to the offending index (e.g. properties[2]). The rare 'partial_creation' error means the entity WAS created but a later step failed: continue with saveproperty.json instead of calling createentity again. - The entity is ALWAYS born with a required 'Name' text property (auto-created — do NOT include it in 'properties'). It comes FIRST in the response 'created.properties' with required:'y': createrecord.json MUST receive a value for it. - STATUSES: the first status of the list is the DEFAULT for new records (records created without a status get it). Records store the status 'id' ('st_...') in the group's docField ('status_main'): send values.status_main = 'st_...' in createrecord/editrecord. Add or edit statuses later with savestatus.json; reordering statuses in the CRM moves the default to the new first one. - ASSOCIATIONS between entities are properties of type 'association' (targetEntity = the entity to link to, which must ALREADY exist — it cannot be the entity being created in this call). To link two NEW entities: create the target entity first, then this one with the association in its 'properties'; or create both and add the association afterwards with saveproperty.json. - For date fields use type 'datetime': record values are ALWAYS stored as unix UTC epoch in SECONDS and shown in the CRM converted to each user's timezone. - The response includes the sanitized 'entity' (every property with its identifier, statusgroups with ids, and the endpoint hints 'property_save', 'status_save', 'entity_edit', 'create', 'records', 'edit') — follow those hints instead of hardcoding routes. - To rename the entity or change its icon/color later use /api/1.0/entities/editentity.json. To edit its records use /api/1.0/entities/editrecord.json. ### entities_save_property `POST https://api.datalyse.io/api/1.0/entities/saveproperty.json` Create OR edit ONE property (field) of a CUSTOM entity ('cust_...'). Without 'identifier' it CREATES a new property (the identifier is generated server-side and returned). With 'identifier' it EDITS that property (partial: only the fields you send change). THIS ENDPOINT IS ALSO THE TOOL TO CREATE ASSOCIATIONS between entities: send type 'association' with 'targetEntity' = the entitykey to link to ('leads', 'companies', another 'cust_...'); records then store the _id of the linked record in that property. One call per property — to add several fields to an EXISTING entity, call it once per field. The INITIAL fields of a NEW entity do not need this endpoint: /api/1.0/entities/createentity.json creates the entity with ALL its properties and statuses in ONE call. Allowed types: text, tags, textarea, select (REQUIRES 'options'), phone, email, number, datetime (date+time, ALWAYS stored as unix UTC epoch in SECONDS — use it for ANY date field; the old type 'date' can no longer be created), country, association (REQUIRES 'targetEntity'). The token's user must be master or admin. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. The linked user must be master or admin. Required. | YOUR_APIKEY | | entitykey | string | yes | Key of the CUSTOM entity ('cust_...') to add/edit the property on. Required. | cust_ab12cd34ef56gh78 | | identifier | string | no | Identifier of an EXISTING property to edit. OMIT to create a new property. (optional) | | | name | string | no | Human name of the property. Required on create; on edit, only sent to rename. | Price | | type | string | no | Property type (create only; the type of an existing property can NEVER be changed): text, tags, textarea, select, phone, email, number, datetime, country, language (lowercase ISO 639-1 code like 'es'/'en'/'pt-br'; the engine auto-provides the options, do NOT send them), currency (lowercase ISO 4217 code like 'eur'/'usd'; options auto-provided), html (rich HTML string; sanitized server-side: script/style/iframe tags, on* attributes and javascript: URLs are stripped), association. Defaults to 'text'. For dates use 'datetime': values are ALWAYS stored as unix UTC epoch in seconds and shown in the CRM converted to each user's timezone. | text | | required | string | no | 'y' to make the property mandatory when creating records. (optional) | n | | unique | string | no | 'y' to enforce unique values across records of the entity (not available for association). (optional) | n | | options | array or JSON string | no | REQUIRED for type select: array of options as plain strings (["Flat","House"]) or {text, textshow, selected:'y'\|'n'} objects; strings are normalized server-side to {text} objects. Record values must be the option 'text'. On edit: omit to keep current options (clearing is not supported); sending them REPLACES the whole list. Also accepts a JSON-encoded string. (optional) | Flat,House,Office | | targetEntity | string | no | REQUIRED for type association: entitykey this property points to ('leads', 'companies', 'opportunities', 'tasks', 'tickets', 'budgets' or another 'cust_...'). Must ALREADY exist. Cannot be changed on edit. | leads | | cardinality | string | no | For type association: 'many-to-one' (default) or 'one-to-many'. (optional) | many-to-one | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/saveproperty.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"cust_ab12cd34ef56gh78","name":"Price","type":"number"}' ``` Create a number field: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "name": "Price", "type": "number" } ``` Create a select with options: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "name": "Type", "type": "select", "options": [ "Flat", "House", "Office" ] } ``` CREATE AN ASSOCIATION to contacts: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "name": "Owner", "type": "association", "targetEntity": "leads" } ``` Edit: rename a property: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "identifier": "ab12777pricexk2m9q4t8w1z5c7", "name": "Sale price" } ``` Edit: replace the options of a select: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "identifier": "ab12777typexk2m9q4t8w1z5c7v", "options": [ "Flat", "House", "Office", "Garage" ] } ``` Create a date+time field (stored as unix UTC epoch): ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "name": "Signing date", "type": "datetime" } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `permission_denied`: The token's linked user is not master or admin - `empty_entitykey`: Missing entitykey parameter - `properties_only_for_custom_entities`: entitykey is a built-in entity: by API, properties can only be managed on custom entities ('cust_...') - `entity_not_found`: No custom entity with that key; the response includes 'valid_entitykeys' - `empty_name`: Creating a property requires a name - `invalid_property_type_`: Unknown type; the response includes 'allowed_types' - `invalid_options`: Type select without valid 'options' (create), or 'options' sent but empty/malformed (edit). Options: non-empty array of strings or {text, textshow, selected} objects - `empty_target_entity`: Type association without 'targetEntity' - `target_entity_not_found`: The targetEntity does not exist in this company; the response includes 'valid_entitykeys'. On CREATE: associations can only point to EXISTING entities — create the target entity first. On EDIT it means the property's STORED targetEntity no longer exists (the target entity was deleted after the association was created; the caller does not need to send targetEntity for this to happen) - `property_not_found`: The identifier sent for edit does not exist (or is deleted); the response includes 'valid_identifiers' - `company_not_found`: Company of the token not found - `server_error`: Unexpected server error - `invalid_cardinality`: cardinality must be 'many-to-one' or 'one-to-many' Notes: - CREATING A NEW ENTITY? Do not chain saveproperty calls to build it: /api/1.0/entities/createentity.json accepts 'properties' (and 'statuses') and creates the FULL entity in ONE call. This endpoint is for adding fields to an entity that ALREADY exists or for editing them. - CREATE: the property identifier is generated server-side (entity prefix + company id + name slug + random) and returned in 'identifier' — use it as the field name in createrecord/editrecord 'values'. New properties are visible in the record profile and tables by default. - EDIT is PARTIAL and safe: only the parameters you send change (name, required, unique, options); everything else keeps its current value. 'type' and 'targetEntity' can NEVER be changed — create a new property instead. Omit 'options' to keep the current ones. - ASSOCIATIONS: after creating a property of type 'association', link records by passing the _id string of the target record as that property's value in createrecord.json/editrecord.json (find ids with getrecords.json, which also returns human-readable 'assoclabels'). The entity's schema lists all its links in 'associations'. - Values of 'select' properties must match one option 'text' exactly; changing options does NOT migrate existing records (old stored values remain and can be re-sent unchanged, but changing a record to an out-of-catalog value is rejected). - The response always includes the full updated sanitized entity — no need to call schemas.json again. - Property NAMES are not deduplicated: creating two properties named 'Price' is allowed and produces two distinct identifiers. Re-sending a create call after a network error therefore creates a DUPLICATE property — check the entity (schemas.json or the 'entity' of the previous response) before retrying. - DATES: 'datetime' is the only date type ('date' was retired 2026-07-21; existing 'date' properties keep working with the same epoch contract). Record values accept unix UTC epoch in seconds (a millisecond epoch is auto-normalized) or 'yyyy-mm-dd' / 'yyyy-mm-dd hh:mm' strings — whatever you send, Mongo ALWAYS stores the unix UTC epoch, so ranges are filterable in getrecords.json with gt/gte/lt/lte just like creation_date. - PROPERTY TYPES & VALUE FORMATS (what to send as the value of each property in createrecord/editrecord 'values'): text/textarea/phone/email -> string; number -> number (or numeric string); select -> the 'text' of one of the property's options (exact match; '' clears); tags -> array of strings (["vip","madrid"]); country -> lowercase ISO-2 code ('es', 'fr'); language -> lowercase ISO 639-1 code ('es', 'en', 'pt-br') from the property's options; currency -> lowercase ISO 4217 code ('eur', 'usd') from the property's options; html -> HTML string (sanitized server-side; the UI renders it with a light rich editor); datetime (and legacy date) -> unix UTC epoch in SECONDS (preferred; ms auto-normalized; 'yyyy-mm-dd' and 'yyyy-mm-dd hh:mm' also accepted) — ALWAYS stored as epoch, shown in each user's timezone; association -> the _id string (24 hex chars) of the target record (find it with getrecords.json); status of the entity -> the status 'id' ('st_...') in the group's docField (usually values.status_main). ### entities_save_status `POST https://api.datalyse.io/api/1.0/entities/savestatus.json` Add OR edit ONE status of a CUSTOM entity ('cust_...'). Without 'id' it ADDS a new status: the FIRST status added to an entity automatically creates its status group (no separate call needed) and becomes the DEFAULT for new records. With 'id' (the 'st_...' id from schemas.json) it EDITS the label/color of an existing status. One call per status — to add several statuses to an EXISTING entity, call it once per status, in the order you want them displayed. The INITIAL statuses of a NEW entity can be created directly in /api/1.0/entities/createentity.json ('statuses' parameter). The token's user must be master or admin. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. The linked user must be master or admin. Required. | YOUR_APIKEY | | entitykey | string | yes | Key of the CUSTOM entity ('cust_...'). Required. | cust_ab12cd34ef56gh78 | | id | string | no | Id ('st_...') of an EXISTING status to edit. OMIT to add a new status. (optional) | | | label | string | no | Status name. Required when adding; on edit, only sent to rename. | Available | | color | string | no | Hex color of the status. (optional) | #22c55e | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/savestatus.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"cust_ab12cd34ef56gh78","label":"Available","color":"#22c55e"}' ``` Add the first status (creates the group, becomes the default): ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "label": "Available", "color": "#22c55e" } ``` Add another status: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "label": "Sold", "color": "#ef4444" } ``` Edit a status label: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "id": "st_1a2b3c4d5e6f7a8b", "label": "Reserved" } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `permission_denied`: The token's linked user is not master or admin - `empty_entitykey`: Missing entitykey parameter - `statuses_only_for_custom_entities`: entitykey is a built-in entity: by API, statuses can only be managed on custom entities ('cust_...') - `empty_status_id`: 'id' was sent but empty. Send a real 'st_...' id to edit, or omit 'id' entirely to add - `empty_label`: The label is missing or blank: adding a status requires a label, and editing with 'label' present requires it to be non-blank (whitespace-only labels are rejected) - `entity_not_found`: No custom entity with that key; the response includes 'valid_entitykeys' - `status not found`: The 'id' sent for edit does not exist in the entity's status group — or the entity has no statuses yet (the response 'detail' says so; omit 'id' and add the first status) - `deleted status cannot be edited`: The status was deleted - `company_not_found`: Company of the token not found - `server_error`: Unexpected server error Notes: - The status group of a custom entity is created AUTOMATICALLY with the first status you add (its docField is 'status_main'). The DEFAULT status is always the FIRST one in display order; reordering statuses in the CRM moves the default accordingly. - Records store the status 'id' ('st_...') in the group's docField: to set a status when creating/editing a record, send values.status_main = 'st_...' in createrecord.json/editrecord.json (schemas.json shows the exact docField and every valid id). Records created without a status get the default one. - The response returns 'status_id' (the id of the created/edited status) and the full updated sanitized entity — chain createrecord.json directly with that id, no extra schemas.json call needed. - Initial statuses — and initial PROPERTIES — can be created directly when creating the entity: createentity.json builds the FULL entity in one call. This endpoint is for adding MORE statuses later or editing existing ones. ### entities_edit_entity `POST https://api.datalyse.io/api/1.0/entities/editentity.json` Edit the METADATA of an entity: rename it (name, name_plural) or change its icon/color. Works on custom entities ('cust_...') and on built-in ones (renaming 'leads' to 'Patients', for example). ONLY metadata: to create/edit PROPERTIES (fields, including associations) use POST /api/1.0/entities/saveproperty.json, and to add/edit STATUSES use POST /api/1.0/entities/savestatus.json — this endpoint rejects the old 'addproperties'/'editproperties'/'addstatuses'/'editstatuses' parameters with use_dedicated_endpoints. The token's user must be master or admin. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. The linked user must be master or admin. Required. | YOUR_APIKEY | | entitykey | string | yes | Key of the entity to edit: 'cust_...' or a built-in key (leads, companies, opportunities, tasks, tickets, budgets). Required. | cust_ab12cd34ef56gh78 | | name | string | no | New singular name. (optional) | Property | | name_plural | string | no | New plural name. (optional) | Properties | | icon | string | no | New Tabler icon class. (optional) | ti ti-home | | color | string | no | New hex color. (optional) | #16a34a | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/entities/editentity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","entitykey":"cust_ab12cd34ef56gh78","name":"Home","name_plural":"Homes"}' ``` Rename an entity: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "name": "Home", "name_plural": "Homes" } ``` Change icon and color: ```json { "token": "YOUR_APIKEY", "entitykey": "cust_ab12cd34ef56gh78", "icon": "ti ti-building", "color": "#7c3aed" } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `permission_denied`: The token's linked user is not master or admin - `empty_entitykey`: Missing entitykey parameter - `use_dedicated_endpoints`: You sent 'addproperties', 'editproperties', 'addstatuses' or 'editstatuses': those operations moved to their own endpoints. Properties (create/edit, associations included): /api/1.0/entities/saveproperty.json. Statuses (add/edit): /api/1.0/entities/savestatus.json. The response includes both routes as 'property_endpoint' and 'status_endpoint' - `empty_name`: 'name' or 'name_plural' sent empty/null - `nothing_to_do`: No metadata parameter sent; the response lists 'expected_any_of' - `entity_not_found`: No entity with that key; the response includes 'valid_entitykeys' - `company_not_found`: Company of the token not found - `server_error`: Unexpected server error Notes: - The entitykey and the physical storage never change, even when renaming. - The response always includes the full updated sanitized entity. - Properties: /api/1.0/entities/saveproperty.json (one call per property; type association creates links between entities). Statuses: /api/1.0/entities/savestatus.json (one call per status). Records: /api/1.0/entities/editrecord.json. ## Hrshifts ### hrshifts_create `POST https://api.datalyse.io/api/1.0/hrshifts/create.json` Create one or many HR shifts in a single atomic request using the same shared core as the live hrshifts_createshift socket. The token user must be master or admin; admins may only target agents inside their authorized departments. Dates use the existing d/m/yyyy socket format (for example 20/7/2026). Each agent's current workplace/timezone is resolved server-side; an optional workplace must match every selected agent. If starttime is equal to or later than endtime, the shift ends the following day. HTTP API creates shifts as saved='y' (published); the live socket UI keeps drafts saved='n'. New shifts are iserro='n'. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. User must be master/admin; scoped app tokens require 'hrshifts'. Required. | YOUR_APIKEY | | agents_ids | array | yes | Unique agent IDs in the same company and authorized department scope. 1-50 required. | 689db0a79957d10338d7e60a | | daystopublish | array | yes | Unique dates in d/m/yyyy or dd/mm/yyyy format. 1-93 required; agent×day combinations max 500. | 20/7/2026 | | starttime | string | yes | Start time HH:MM (24-hour). Required. | 05:18 | | endtime | string | yes | End time HH:MM. Required. Equal/earlier than start means next-day end. | 05:18 | | workplace | string | no | Optional compatibility field from modal/hrshifts: must match every selected agent's current workplace. Timezone is always loaded server-side. (optional) | 679383c5e5002a43d9562657 | | comment | string | no | Optional plain-text comment, max 500; HTML/control characters rejected. (optional) | | | idempotency_key | string | no | Optional stable retry key, 8-128 safe characters. Same key+payload returns original IDs; changed payload returns conflict. (optional) | hrshift:create:20-7-2026:0518 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/hrshifts/create.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","agents_ids":["689db0a79957d10338d7e60a"],"daystopublish":["20/7/2026"],"starttime":"05:18","endtime":"05:18","workplace":"679383c5e5002a43d9562657","comment":"","idempotency_key":"hrshift:create:20-7-2026:0518"}' ``` Create shift using the live socket-compatible format: ```json { "token": "YOUR_APIKEY", "agents_ids": [ "689db0a79957d10338d7e60a" ], "daystopublish": [ "20/7/2026" ], "starttime": "05:18", "endtime": "05:18", "workplace": "679383c5e5002a43d9562657", "comment": "", "idempotency_key": "hrshift:create:20-7-2026:0518" } ``` Errors: - HTTP 400 — `invalid_agents_ids \| invalid_days \| invalid_time \| invalid_comment \| invalid_saved \| invalid_iserro \| invalid_idempotency_key \| agent_workplace_not_configured \| workplace_not_found \| workplace_mismatch \| invalid_datetime`: Input failed validation or selected agents/workplace are inconsistent - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| client_id_not_found`: Missing or invalid token - HTTP 403 — `permission_denied \| permission_denied_agent \| scope_denied`: User is not master/admin or targets an agent outside department scope - HTTP 409 — `duplicate_shift \| idempotency_conflict`: An identical timeslot already exists or retry key was reused with changed payload - HTTP 413 — `too_many_shifts`: More than 500 agent×day combinations - HTTP 500 — `internal_error \| insert_failed`: Transactional persistence failure Notes: - HTTP and the live hrshifts_createshift socket call the same hr_createShiftsCore (same RBAC, validation, duplicates and transaction). - HTTP marks actor.fromapi='y' so created shifts persist as saved='y'; the socket UI path keeps draft saved='n'. - All requested shifts are inserted in one Mongo transaction; a validation/duplicate failure creates none. - The workplace sent by clients is never trusted as ownership: selected agents and locations are loaded by company_id and department RBAC. - starttime == endtime preserves the socket behavior and creates a 24-hour overnight shift. - Use idempotency_key for API/AI retries. ## Leads ### add_note_to_lead `POST https://api.datalyse.io/api/1.0/leads/addnote.json` Add a note to the contact or company | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | lead_id | string | yes | ID of the contact or company | | | text | string | yes | Text of the note | Hello world | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/leads/addnote.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","lead_id":"","text":"Hello world"}' ``` ### get_lead_activity `POST https://api.datalyse.io/api/1.0/leads/getactivity.json` Get activity/timeline for a contact | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | lead_id | string | yes | ID of the contact | | | type | string | no | Filter by activity type: callin, callout, email, emailin, emailout, note, sms, form_completed, etc. (optional) | | | var_resultspage | string | yes | Maximum number of results to display | 20 | | var_page | string | yes | Page number | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/leads/getactivity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","lead_id":"","var_resultspage":"20","var_page":"1"}' ``` ## Notifications ### notifications_list `POST https://api.datalyse.io/api/1.0/notifications/list.json` Get notifications for the authenticated agent with pagination | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | var_resultspage | string | yes | Results per page (default 20) | 20 | | var_page | string | yes | Page number (default 1) | 1 | | read | string | no | Filter by read status: 0 for unread, 1 for read (optional, returns all by default) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/notifications/list.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","var_resultspage":"20","var_page":"1"}' ``` ### notifications_sendpush `POST https://api.datalyse.io/api/1.0/notifications/sendpush.json` Send a push notification to an agent. Same logic as workflows sendnoti block | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | title | string | yes | Notification title | | | text | string | yes | Notification text/body | | | target_agent_id | string | no | Agent ID to send the notification to (optional, defaults to self) | | | assign_to | string | yes | Set to 'current' to send to the lead's owner agent (requires id_lead) | | | id_lead | string | no | Lead ID to link in the notification and resolve owner agent (optional) | | | type | string | yes | Notification type (default 'noti') | noti | | badge | string | yes | Badge letter shown in notification (default 'L') | L | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/notifications/sendpush.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","title":"","text":"","assign_to":"","type":"noti","badge":"L"}' ``` ### notifications_read `POST https://api.datalyse.io/api/1.0/notifications/read.json` Mark one or all notifications as read | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | notification_id | string | no | ID of a single notification to mark as read (optional if read_all is used) | | | read_all | string | yes | Set to 'y' to mark all notifications as read | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/notifications/read.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","read_all":""}' ``` ## Opportunities ### add_note_to_opportunity `POST https://api.datalyse.io/api/1.0/opportunities/addnote.json` Add a note to an opportunity | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | opportunity_id | string | yes | ID of the opportunity | | | text | string | yes | Text of the note | Hello world | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/opportunities/addnote.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","opportunity_id":"","text":"Hello world"}' ``` ### get_opportunity_activity `POST https://api.datalyse.io/api/1.0/opportunities/getactivity.json` Get activity/timeline for an opportunity | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | opportunity_id | string | yes | ID of the opportunity | | | type | string | no | Filter by activity type (optional) | | | var_resultspage | string | yes | Maximum number of results to display | 20 | | var_page | string | yes | Page number | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/opportunities/getactivity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","opportunity_id":"","var_resultspage":"20","var_page":"1"}' ``` ## Payments ### create_payment `POST https://api.datalyse.io/api/1.0/payments/create.json` Create a new payment record | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | agent_id | string | yes | Agent ID | | | id_lead | string | yes | ID of the contact or company | | | fromagent_id | string | yes | Agent ID who created the payment | | | ispayment | string | yes | Payment type flag | | | mt4_transtype | string | yes | Transaction type | | | name | string | yes | Payment name or description | | | mt4user | string | no | MT4 user ID (optional) | | | amount | string | yes | Payment amount | 0 | | amount_usd | string | no | Amount in USD (optional) | | | amount_eur | string | no | Amount in EUR (optional) | | | currency | string | yes | Currency code | eur | | netcurrency | string | no | Net currency (optional) | | | comission | string | no | Commission amount (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/payments/create.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","agent_id":"","id_lead":"","fromagent_id":"","ispayment":"","mt4_transtype":"","name":"","amount":"0","currency":"eur"}' ``` ### get_payments `POST https://api.datalyse.io/api/1.0/payments/get.json` Get a paginated list of payments/credits for the authenticated company. Supports filtering by payment type, lead, agent, and date range (datefrom/dateto/timezone). Dates are filtered against the payment 'date' field (unix timestamp in seconds). Use this to answer questions like 'show me payments last month', 'list credits for lead X', or 'how many payments did agent Y process in January'. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | var_resultspage | string | yes | Maximum number of results to display | 20 | | var_page | string | yes | Page number | 1 | | ispayment | string | no | Filter by type: "y" for payments, "n" for credits (optional) | | | id_lead | string | no | Filter by lead ID (optional) | | | agent_id | string | no | Filter by agent ID (optional) | | | datefrom | string | no | Start date for filtering results by payment date (optional, format: YYYY-MM-DD, example: 2026-01-01). Requires dateto and timezone to apply. | | | dateto | string | no | End date for filtering results by payment date (optional, format: YYYY-MM-DD, example: 2026-12-31). Requires datefrom and timezone to apply. | | | timezone | string | no | Timezone for date filtering (optional, example: Europe/Madrid). Required together with datefrom and dateto. | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/payments/get.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","var_resultspage":"20","var_page":"1"}' ``` ### edit_payment `POST https://api.datalyse.io/api/1.0/payments/edit.json` Edit a payment | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | payment_id | string | yes | ID of the payment to edit | | | name | string | no | Payment name (optional) | | | agent_id | string | no | Agent ID (optional) | | | amount | string | no | Payment amount (optional) | | | amount_usd | string | no | Amount in USD (optional) | | | amount_eur | string | no | Amount in EUR (optional) | | | currency | string | no | Currency code (optional) | | | ispayment | string | no | Payment type flag (optional) | | | mt4_transtype | string | no | Transaction type (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/payments/edit.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","payment_id":""}' ``` ### delete_payment `POST https://api.datalyse.io/api/1.0/payments/delete.json` Delete a payment (soft delete) | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | payment_id | string | yes | ID of the payment to delete | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/payments/delete.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","payment_id":""}' ``` ## Phonenumbers ### phonenumbers_get_countries `GET https://api.datalyse.io/api/1.0/phonenumbers/countries.json` Get all countries with phone numbers available for purchase: ISO code, name, phone prefix, available number types and how many area groups each one has. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/countries.json?token=YOUR_APIKEY' ``` ### phonenumbers_get_did_groups `GET https://api.datalyse.io/api/1.0/phonenumbers/did_groups.json` Get purchasable number groups (area codes) of a country with client prices in EUR. Each group lists its SKUs (capacity plans): setup and monthly price, included channels, per-minute incoming cost (coste_xmin/coste_xllam, 0 for flat-rate plans) and whether the plan is sellable. needs_registration=true means identity documents are required after purchase. Global/UIFN groups are returned for any country. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | country | string | yes | Country ISO2 code (e.g. ES, GB, US) | ES | | type | string | no | Filter by group type: Local, National, Mobile, Toll-free, Shared Cost, Global (optional) | | | q | string | no | Search by area name or prefix (optional) | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/did_groups.json?token=YOUR_APIKEY&country=ES&type=&q=' ``` ### phonenumbers_get_available_dids `GET https://api.datalyse.io/api/1.0/phonenumbers/available_dids.json` Get the specific numbers available to pick inside a DID group (live from the provider). If the list is empty a new random number can still be ordered (new_number_available is always true). Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | did_group | string | yes | DID group id (from did_groups.json) | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/available_dids.json?token=YOUR_APIKEY&did_group=' ``` ### phonenumbers_order `POST https://api.datalyse.io/api/1.0/phonenumbers/order.json` Buy a phone number: creates the order at the provider, charges the voice credit balance and assigns the line to a PBX (creates a new PBX with a default IVR if none is given). If needs_registration is true the number requires identity verification (see requirements, identity and identity_submit endpoints). The line starts as activating (estado 0) and switches to active automatically when the provider confirms. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | did_group_id | string | yes | DID group id (from did_groups.json) | | | sku_id | string | yes | Capacity plan SKU id of the group (from did_groups.json, only sellable SKUs) | | | available_did_id | string | no | Specific number picked from available_dids.json; omit to get a new random number (optional) | | | idcampanadondecrear | integer | no | PBX id where to attach the number; omit to create a new PBX with a default IVR (optional) | | | lang | string | no | 2-letter language for the default IVR TTS voice when a new PBX is created (optional, default en) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/order.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","did_group_id":"","sku_id":""}' ``` ### phonenumbers_get_requirements `GET https://api.datalyse.io/api/1.0/phonenumbers/requirements.json` Get the regulatory registration requirements of a DID group: accepted identity/address proof types, mandatory identity fields and permanent document templates. Returns needs_registration=false with an empty list when no registration is needed. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | did_group_id | string | yes | DID group id (from did_groups.json) | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/requirements.json?token=YOUR_APIKEY&did_group_id=' ``` ### phonenumbers_create_identity `POST https://api.datalyse.io/api/1.0/phonenumbers/identity.json` Create a regulatory identity with its address at the provider (needed for numbers with needs_registration). Returns identity_id and address_id to use with identity_document.json and identity_submit.json. The identity is saved and reusable for future numbers (see identities_list.json). Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | identity.identity_type | string | yes | Identity type: personal or business | personal | | identity.first_name | string | yes | First name (or legal representative for business) | | | identity.last_name | string | yes | Last name | | | identity.phone_number | string | yes | Contact phone number with international prefix | | | identity.company_name | string | yes | Company name (business identities) | | | identity.company_reg_number | string | yes | Company registration number (if required by the country) | | | identity.vat_id | string | yes | VAT id (if required by the country) | | | identity.personal_tax_id | string | yes | Personal tax id (if required by the country) | | | identity.id_number | string | yes | Personal id/passport number (if required by the country) | | | identity.birth_date | string | yes | Birth date YYYY-MM-DD (if required by the country) | | | identity.contact_email | string | no | Contact email (optional) | | | address.country_iso | string | yes | Address country ISO2 code | ES | | address.city_name | string | yes | City | | | address.postal_code | string | yes | Postal code | | | address.address | string | yes | Street address | | | address.description | string | no | Address description (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/identity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","identity.identity_type":"personal","identity.first_name":"","identity.last_name":"","identity.phone_number":"","identity.company_name":"","identity.company_reg_number":"","identity.vat_id":"","identity.personal_tax_id":"","identity.id_number":"","identity.birth_date":"","address.country_iso":"ES","address.city_name":"","address.postal_code":"","address.address":""}' ``` ### phonenumbers_list_identities `GET https://api.datalyse.io/api/1.0/phonenumbers/identities_list.json` List the client's saved regulatory identities with their addresses and verification status, to reuse them when registering another number. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/identities_list.json?token=YOUR_APIKEY' ``` ### phonenumbers_create_address `POST https://api.datalyse.io/api/1.0/phonenumbers/address.json` Add an additional address to one of the client's existing regulatory identities (identity reuse for a number in another city/country). Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | identity_id | string | yes | Identity id owned by the client (from identities_list.json) | | | address.country_iso | string | yes | Address country ISO2 code | ES | | address.city_name | string | yes | City | | | address.postal_code | string | yes | Postal code | | | address.address | string | yes | Street address | | | address.description | string | no | Address description (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/address.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","identity_id":"","address.country_iso":"ES","address.city_name":"","address.postal_code":"","address.address":""}' ``` ### phonenumbers_upload_identity_document `POST https://api.datalyse.io/api/1.0/phonenumbers/identity_document.json` Upload a verification document (multipart form-data, field name: file; pdf/jpg/png, max 20MB). The file is encrypted client-side before being sent to the provider. kind=proof_identity links it to an identity (needs proof_type_id), kind=proof_address to an address (needs proof_type_id and address_id), kind=permanent_document to a permanent template (needs template_id). Proof type and template ids come from requirements.json. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | file | file | yes | Document file (multipart form-data; pdf, jpg or png, max 20MB) | | | kind | string | yes | Document kind: proof_identity, proof_address or permanent_document | proof_identity | | identity_id | string | yes | Identity id owned by the client (required for proof_identity and permanent_document) | | | proof_type_id | string | yes | Proof type id from requirements.json (required for proof_identity and proof_address) | | | address_id | string | yes | Address id the proof belongs to (required for proof_address) | | | template_id | string | yes | Permanent document template id from requirements.json (required for permanent_document) | | | description | string | no | File description (optional) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/identity_document.json' \ -F 'token=YOUR_APIKEY' \ -F 'file=@/path/to/file.pdf' \ -F 'kind=proof_identity' \ -F 'identity_id=' \ -F 'proof_type_id=' \ -F 'address_id=' \ -F 'template_id=' ``` ### phonenumbers_submit_identity `POST https://api.datalyse.io/api/1.0/phonenumbers/identity_submit.json` Submit the address verification of a purchased number for provider review. Optionally pre-validates against the address requirement so missing data is reported instantly (validation_failed) instead of days later as a rejection. On approval the line is activated automatically and the user gets a push notification. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | did_id | string | yes | DID id of the purchased number (returned by order.json) | | | address_id | string | yes | Address id of one of the client's identities | | | identity_id | string | no | Identity id, when the address alone is ambiguous (optional) | | | address_requirement_id | string | no | Address requirement id from requirements.json to pre-validate before submitting (optional, recommended) | | | service_description | string | no | Short description of the service using the number (optional, default: CRM phone line) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/identity_submit.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","did_id":"","address_id":""}' ``` ### phonenumbers_get_identity_status `GET https://api.datalyse.io/api/1.0/phonenumbers/identity_status.json` Get the live verification status of a number registration (pending, approved or rejected with reject reasons). If the provider already approved it, the line is activated on the spot. Pass did_id or verification_id. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | did_id | string | yes | DID id of the number (alternative to verification_id) | | | verification_id | string | yes | Verification id returned by identity_submit.json (alternative to did_id) | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/identity_status.json?token=YOUR_APIKEY&did_id=&verification_id=' ``` ### phonenumbers_get_pending_registrations `GET https://api.datalyse.io/api/1.0/phonenumbers/pending_registrations.json` Get the client's numbers with identity registration pending (not sent, in review or rejected) plus the numbers still activating (bought without registration, waiting for provider confirmation, last 72h). Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/pending_registrations.json?token=YOUR_APIKEY' ``` ### phonenumbers_get_capacity_info `GET https://api.datalyse.io/api/1.0/phonenumbers/capacity_info.json` Get the current channel capacity of a phone line (included and dedicated channels) and the monthly price per extra dedicated channel in EUR. pool_disponible=false means extra channels are not available for that country. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | phoneline | string | yes | Phone line number (digits only, with country prefix) | 34930000000 | Example: ```bash curl 'https://api.datalyse.io/api/1.0/phonenumbers/capacity_info.json?token=YOUR_APIKEY&phoneline=34930000000' ``` ### phonenumbers_contract_channels `POST https://api.datalyse.io/api/1.0/phonenumbers/contract_channels.json` Buy extra dedicated channels for a phone line (1 to 50). Charges the voice credit balance, expands the DID capacity at the provider and adds the price to the line's monthly renewal. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | phoneline | string | yes | Phone line number (digits only, with country prefix) | 34930000000 | | channels | integer | yes | Number of dedicated channels to add (1-50) | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/contract_channels.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","phoneline":"34930000000","channels":"1"}' ``` ### phonenumbers_move `POST https://api.datalyse.io/api/1.0/phonenumbers/move.json` Move a phone number to another PBX of the same client, or create a new PBX (with a default IVR) and move it there. Identify the line with phoneline or id_alias; set to_pbx_id for an existing PBX or new_pbx=true to create one. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | phoneline | string | yes | Phone line number to move (alternative to id_alias) | 34930000000 | | id_alias | integer | yes | Internal line id (alternative to phoneline) | | | to_pbx_id | integer | yes | Destination PBX id (required unless new_pbx is true) | | | new_pbx | boolean | yes | Set to true to create a new PBX and move the number there | | | new_pbx_name | string | no | Name for the new PBX (optional, only with new_pbx) | | | lang | string | no | 2-letter language for the new PBX default IVR TTS voice (optional, default en) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/move.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","phoneline":"34930000000","id_alias":"","to_pbx_id":"","new_pbx":""}' ``` ### phonenumbers_clone_pbx `POST https://api.datalyse.io/api/1.0/phonenumbers/clone_pbx.json` Clone a PBX: campaign, configuration and all call-flow nodes (node links are remapped). Phone numbers are NOT cloned. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | pbx_id | integer | yes | PBX id to clone (must belong to the client) | | | new_name | string | no | Name for the cloned PBX (optional, default: original name + (copy)) | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/clone_pbx.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","pbx_id":""}' ``` ### phonenumber_delete `POST https://api.datalyse.io/api/1.0/phonenumbers/delete.json` Cancel a phone line: terminates the number at the provider and removes the line. The purchase is refunded in credits only if cancelled the same month it was bought. The cancellation is irreversible. Requires phone numbers management permission. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | phoneline | string | yes | Phone line number to cancel (digits only, with country prefix) | 34930000000 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/phonenumbers/delete.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","phoneline":"34930000000"}' ``` ## Sendsms ### send_sms `POST https://api.datalyse.io/api/1.0/sendsms/smsmt.json` Send a transactional SMS (SMS-MT, mobile terminated) to a single phone number through the company's SMS provider. Use this when the user asks to send an SMS, a short text, a verification code, an OTP, a reminder, a notification or any text message to a phone number. Before calling this, the caller must already know the destination phone number in international format (country code + number, no '+' or spaces), the text body, and a 'from' sender ID (alphanumeric up to 11 chars, e.g. 'MyCompany', or a numeric shortcode). If you do not have a valid sender, ask the user which 'from' to use — do NOT invent one. To estimate the cost per destination country before sending, call /api/1.0/sendsms/prices.json first. Optionally attach a lead_id so the message is logged inside that lead's timeline. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | to | string | yes | Recipient phone number in international format, digits only, with country code and no '+' sign or spaces (e.g. 34600000000 for Spain, 14155552671 for USA). Required. | 34682288834 | | from | string | yes | Sender ID displayed to the recipient. Alphanumeric up to 11 characters (e.g. 'Datalyse', 'Acme'), or a numeric shortcode. Some carriers strip or rewrite alphanumeric senders — ask the user for a valid one if unsure. Required. | testfrom | | text | string | yes | UTF-8 body of the SMS. Keep under 160 GSM-7 characters to fit in one segment; longer texts are split into multiple segments and billed per segment. Required. | hello world | | lead_id | string | no | Optional Mongo ObjectId of the lead to attach this SMS to. When provided, the message is stored inside that lead's activity timeline. Optional. | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendsms/smsmt.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","to":"34682288834","from":"testfrom","text":"hello world"}' ``` ### get_sms_prices `GET https://api.datalyse.io/api/1.0/sendsms/prices.json` Get SMS pricing list, optionally filtered by country | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | country | string | no | Country ISO code to filter prices (optional) | | Example: ```bash curl 'https://api.datalyse.io/api/1.0/sendsms/prices.json?token=YOUR_APIKEY&country=' ``` ## Sendvoicecall ### sendvoicecall_listaiagents `POST https://api.datalyse.io/api/1.0/sendvoicecall/listaiagents.json` List every configured AI voice agent with id, name, language, voice, system-prompt summary and knowledge base. Call this before the enabled createcall endpoint to select the agent_ai_id matching the user's requested call purpose. This endpoint itself does not place calls. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendvoicecall/listaiagents.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### sendvoicecall_createcall `POST https://api.datalyse.io/api/1.0/sendvoicecall/createcall.json` Create a new AI voice call batch campaign that will phone one or more recipients using an AI agent. Use this when the user wants the system to actually place outbound phone calls (e.g. 'call these leads', 'phone these customers', 'launch a reminder call campaign'). MANDATORY WORKFLOW: (1) call /api/1.0/sendvoicecall/listaiagents.json to pick the right 'agent_ai_id' for the campaign's purpose; (2) call /api/1.0/companyuserdata/callerids.json to pick a valid 'caller_id' (one of the company's phone lines allowed to place outbound calls); (3) build the 'recipients' array with one entry per person to call, each with phone (international format, digits only), name and country ISO code. Optionally schedule the campaign: set mode='schedule' plus start_date/start_time/end_time/timezone, or mode='immediately' to dial as soon as the worker picks it up. Returns a batch_id that can be later used with getone.json, stop.json, pause.json and unpause.json. Never invent phone numbers — only use the ones the user explicitly provided or the ones retrieved from a previous query. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | agent_ai_id | string | yes | Mongo ObjectId of the AI voice agent that will conduct the calls. Obtain from /api/1.0/sendvoicecall/listaiagents.json. Required. | | | caller_id | string | yes | Phone number (caller id) the AI will dial out from. Must be one of the callerids available to the company — obtain from /api/1.0/companyuserdata/callerids.json. Can also be a shared caller id in the format 'compartido 999id_campana'. Required. | | | batch_name | string | no | Human-readable name for this call batch (shown in the UI and reports). Optional. | | | recipients | array | yes | JSON array of call targets. Each element is an object with three string fields: 'phone' (international format, digits only, no '+' and no spaces, e.g. 34660558411), 'name' (display name, e.g. Marc) and 'country' (ISO 3166-1 alpha-2 lowercase, e.g. es, us, mx). At least one recipient is required. Pass it as a real JSON array of objects — do NOT wrap it in a string. See the 'default' field for a concrete example. | [object Object],[object Object] | | mode | string | yes | Dispatch mode. 'immediately' (default) starts dialing as soon as the worker picks up the batch; 'schedule' uses the start_date/start_time/end_time/timezone fields below. Required. | immediately | | timezone | string | no | IANA timezone for start_date/start_time/end_time when mode='schedule' (e.g. 'Europe/Madrid', 'America/Mexico_City'). Optional, defaults to 'UTC'. | UTC | | start_date | string | no | Date the campaign can start dialing, in YYYY-MM-DD format. Only used when mode='schedule'. Optional, defaults to today. | | | start_time | string | no | Earliest hour of the day at which the AI is allowed to place calls, in 24h HH:MM format. Only used when mode='schedule'. Optional, defaults to '10:00'. | 10:00 | | end_time | string | no | Latest hour of the day at which the AI is allowed to place calls, in 24h HH:MM format. Only used when mode='schedule'. Optional, defaults to '19:00'. | 19:00 | | rate_per_minute | string | no | Maximum number of simultaneous new calls the dialer is allowed to open per minute. Use to throttle spend and avoid carrier rate-limits. Optional, defaults to '30'. | 30 | | date_start_ms | string | no | Alternative to start_date/start_time: absolute Unix timestamp in MILLISECONDS (UTC) at which dialing should begin. Optional. | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendvoicecall/createcall.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","agent_ai_id":"","caller_id":"","recipients":[{"name":"marc","phone":"34660558411","country":"es"},{"name":"James","phone":"34682288834","country":"es"}],"mode":"immediately","timezone":"UTC","start_time":"10:00","end_time":"19:00","rate_per_minute":"30"}' ``` ## Sendwhatsapp ### whatsapp_get_sessions_and_templates `POST https://api.datalyse.io/api/1.0/sendwhatsapp/getsessionsandtemplates.json` List every connected WhatsApp Business session and its approved Meta templates. Call this before sending free-form messages, files or templates: use the returned session _id as idses and inspect template name, language and components before template dispatch. Returns discovery data only; the separate enabled send endpoints perform dispatch. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendwhatsapp/getsessionsandtemplates.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY"}' ``` ### sendwhatsapp_sendmsgtemplate `POST https://api.datalyse.io/api/1.0/sendwhatsapp/sendmsgtemplate.json` Send a pre-approved WhatsApp Business template message directly through the Meta Cloud API and return Meta's response synchronously (including the delivered message wamid on success, or Meta's error on failure). Use this to start conversations outside the 24-hour customer service window, to send transactional notifications (OTP, order updates, shipping, reminders), or to re-engage a lead when the free-form chat window is closed. MANDATORY WORKFLOW: (1) first call /api/1.0/sendwhatsapp/getsessionsandtemplates.json to discover the sessions and their approved templates; (2) pick the session 'idses' the user wants to send from and the exact 'template_name' that matches the intent; (3) inspect that template's 'components' array — if any component contains {{1}}, {{2}}, {{3}}... body variables, pass the matching values inside the simplified 'variables' field (the server auto-inspects the template and places each value in the correct Meta slot — you do NOT need to build the Meta components array yourself). If the template has ZERO body variables and no TEXT header variable, simply omit 'variables' entirely. IMPORTANT — MEDIA HEADERS (IMAGE/VIDEO/DOCUMENT): do NOT ask the user for a media URL and do NOT send 'variables.header' for these. The server auto-loads the media from the file that was originally uploaded with the template (infinitycloud), falling back to Meta's stored example. Only pass 'variables.header' as a media URL if the user explicitly asks to override the template's attached file with a different one. Never invent variable values — if the user did not provide body/text values, ask first. Unlike the old queue-based behaviour, this endpoint now POSTs to Meta synchronously: a successful response returns 'wamid' from Meta, and a failure returns status:'error' with the Meta error embedded. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | idses | string | yes | WhatsApp session _id (24-char Mongo ObjectId) identifying which WhatsApp Business number sends the message. Obtain it from /api/1.0/sendwhatsapp/getsessionsandtemplates.json. Required. | | | to | string | yes | Recipient phone number in full international format, digits only, no '+' sign, no spaces, no dashes (e.g. 34600000000). Required. | 34682288834 | | template_name | string | yes | Exact 'name' field of the approved WhatsApp template to send (e.g. 'datalysewowia'). Must be one of the templates returned by getsessionsandtemplates for the chosen session. Required. | | | language | string | no | Template language code (e.g. 'es', 'en_US', 'pt_BR'). Optional: defaults to the template's own language. Only override it if you know the same template has multiple approved translations. | | | variables | object | no | Simplified template variables — the server auto-inspects the approved template's components and places each value in the correct Meta slot, so you do NOT build the Meta components array yourself. Shape: an object with any of these keys — 'body' is an array of strings matching the template BODY's {{1}},{{2}},... placeholders in order (e.g. body:['Marc','OFFER105']); 'header' is used ONLY when the template HEADER is TEXT with a {{1}} variable (value is the plain text) OR when the caller wants to explicitly override an IMAGE/VIDEO/DOCUMENT media template with a different public URL (normally you should NOT send 'header' for media templates — the server auto-loads the file that was uploaded with the template); 'button' is an array of URL-button values where each element is either a plain string or an object {index:N, payload:'...'} for button parameter {{1}}. Shortcuts: pass a bare array of strings (['123456','Marc']) to fill only body variables, or a bare string ('123456') for a single body variable. Count and order of 'body' entries MUST match the {{n}} placeholders in the template's BODY text — use /api/1.0/sendwhatsapp/getsessionsandtemplates.json to inspect the template first. Omit this field entirely when the template has no body variables. | [object Object] | | components | array | no | ADVANCED / OPTIONAL escape hatch. Pre-built Meta Cloud API components array in the exact Meta shape (entries like {type:'body', parameters:[{type:'text', text:'...'}]}). Only use this if you are replicating an existing hand-crafted Meta payload — normally you should use the simpler 'variables' field instead and let the server build the components automatically. If both 'components' and 'variables' are provided, 'components' wins. | [object Object] | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendwhatsapp/sendmsgtemplate.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","idses":"6797065fdfec41b50d649e64","to":"34660558411","template_name":"sample_template"}' ``` Template WITHOUT variables: ```json { "token": "YOUR_APIKEY", "idses": "6797065fdfec41b50d649e64", "to": "34660558411", "template_name": "sample_template" } ``` Template with ONE body variable {{1}} (simplified 'variables'): ```json { "token": "YOUR_APIKEY", "idses": "6797065fdfec41b50d649e64", "to": "34660558411", "template_name": "authcodenow", "variables": [ "123456" ] } ``` Template with TEXT header variable + body variables (simplified 'variables'): ```json { "token": "YOUR_APIKEY", "idses": "6797065fdfec41b50d649e64", "to": "34660558411", "template_name": "datalysewowia", "variables": { "header": "Header value", "body": [ "Marc", "OFFER105" ] } } ``` Template with BODY variable only (re-engage after 24h): ```json { "token": "YOUR_APIKEY", "idses": "YOUR_SESSION_ID", "to": "34600000000", "template_name": "mensajecuandopasanlas24holacomoestas", "variables": [ "HOLA" ] } ``` Template with VIDEO/IMAGE/DOCUMENT header — no 'variables.header' needed: ```json { "token": "YOUR_APIKEY", "idses": "YOUR_SESSION_ID", "to": "34600000000", "template_name": "templatenewfeatureannouncement", "language": "es" } ``` Template with media header + body variables + URL button (media auto-loaded): ```json { "token": "YOUR_APIKEY", "idses": "6797065fdfec41b50d649e64", "to": "34660558411", "template_name": "templateshipmentupdate", "variables": { "body": [ "Emily", "OFFER105", "31/12/2025" ], "button": [ { "index": 0, "payload": "promo2025" } ] } } ``` Explicit media override (rare — only when user wants a different file): ```json { "token": "YOUR_APIKEY", "idses": "6797065fdfec41b50d649e64", "to": "34660558411", "template_name": "templateshipmentupdate", "variables": { "header": "https://example.com/override-banner.jpg", "body": [ "Emily", "OFFER105", "31/12/2025" ] } } ``` Errors: - `EMPTY_TOKEN`: Missing token parameter - `error_apitokennotfound`: Token not found or not authorized - `empty_idses`: Missing idses parameter - `empty_to`: Missing to parameter - `empty_template_name`: Missing template_name parameter - `invalid_idses`: idses is not a valid ObjectId - `session_not_found`: Session does not exist for this company or has no meta_whats_id - `session_missing_meta_credentials`: The session document does not have accestoken or meta_phone_number_id set; cannot reach Meta Cloud API - `template_not_found`: No approved template with that name exists for this session's WhatsApp Business Account - `missing_body_variables`: The template BODY has {{n}} placeholders but not enough values were provided in 'variables.body' (the response includes expected and received counts) - `missing_header_media`: The template HEADER format is IMAGE/VIDEO/DOCUMENT and the server could not auto-load a media file for it (no upload record in the uploads collection and no example.header_handle on the template). This should be rare — it means the template was approved without an attached media file. The caller can unblock it by passing 'variables.header' with a public media URL. - `missing_header_variable`: The template HEADER is TEXT with a {{1}} placeholder but no 'variables.header' value was provided - `meta_request_failed`: Transport-level failure when reaching graph.facebook.com (network/timeout) - `meta_api_error`: Meta Cloud API returned a 4xx/5xx or an error payload. The response includes http_status, meta_error (the full Meta error object) and sent_payload for debugging Notes: - Dispatch is now SYNCHRONOUS: the handler POSTs directly to https://graph.facebook.com/v20.0/{phone_number_id}/messages using the session's accestoken, and waits for Meta's response before replying. On success the response includes 'wamid' (Meta's message id) and the full 'meta_response'; on failure it includes 'meta_error'. - Prefer the simplified 'variables' field over 'components'. The server reads the template's BODY/HEADER/BUTTONS metadata and auto-builds the correct Meta components array — you only need to supply raw values. - Common Meta error causes (132xxx): (1) wrong number of parameters vs template definition, (2) passing text for a media header or vice versa, (3) URL button payload length/format not matching the approved template, (4) template language code mismatch. ### whatsapp_send_message `POST https://api.datalyse.io/api/1.0/sendwhatsapp/sendmsg.json` Send a free-form WhatsApp text message from a company WhatsApp Business number. This works only inside Meta's 24-hour customer-service window; when it is closed, use the enabled sendmsgtemplate endpoint with an approved template. Obtain idses from getsessionsandtemplates.json and send exactly the recipient and text requested by the user. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | idses | string | yes | WhatsApp session _id (24-char Mongo ObjectId) identifying which WhatsApp Business number sends the message. Obtain from /api/1.0/sendwhatsapp/getsessionsandtemplates.json. Required. | | | to | string | yes | Recipient phone number in full international format, digits only, no '+' sign or spaces (e.g. 34600000000). Required. | 34682288834 | | text | string | yes | UTF-8 body of the WhatsApp message. Supports WhatsApp's basic markdown (*bold*, _italic_, ~strike~, ```mono```). Required. | hello world | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendwhatsapp/sendmsg.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","idses":"","to":"34682288834","text":"hello world"}' ``` ### whatsapp_send_file `POST https://api.datalyse.io/api/1.0/sendwhatsapp/sendfile.json` Send a media/document file by WhatsApp. The file must be on a public HTTPS URL Meta can fetch with the correct Content-Type. This works inside the 24-hour customer-service window; outside it, use the enabled template endpoint when the approved template supports that media. Obtain idses first and use only the recipient/file explicitly requested. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token of the company. Required. | YOUR_APIKEY | | idses | string | yes | WhatsApp session _id (24-char Mongo ObjectId) identifying which WhatsApp Business number sends the file. Obtain from /api/1.0/sendwhatsapp/getsessionsandtemplates.json. Required. | | | to | string | yes | Recipient phone number in full international format, digits only, no '+' sign or spaces (e.g. 34600000000). Required. | 34682288834 | | file_url | string | yes | Publicly reachable HTTPS URL of the file to send. Meta will fetch it; the host must serve the file without authentication and with the correct Content-Type header. Required. | | | filename | string | no | Display filename (including extension) shown to the recipient — especially important for PDFs and documents. Optional for images/videos. | | | caption | string | no | Optional caption text that appears under the media (images/videos only; ignored for audio and most document types). | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/sendwhatsapp/sendfile.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","idses":"","to":"34682288834","file_url":""}' ``` ## Tasks ### add_note_to_task `POST https://api.datalyse.io/api/1.0/tasks/addnote.json` Add a note to a task | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | task_id | string | yes | ID of the task | | | text | string | yes | Text of the note | Hello world | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/tasks/addnote.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","task_id":"","text":"Hello world"}' ``` ### get_task_activity `POST https://api.datalyse.io/api/1.0/tasks/getactivity.json` Get activity/timeline for a task | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | task_id | string | yes | ID of the task | | | type | string | no | Filter by activity type (optional) | | | var_resultspage | string | yes | Maximum number of results to display | 20 | | var_page | string | yes | Page number | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/tasks/getactivity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","task_id":"","var_resultspage":"20","var_page":"1"}' ``` ## Tickets ### add_note_to_ticket `POST https://api.datalyse.io/api/1.0/tickets/addnote.json` Add a note to a ticket | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | ticket_id | string | yes | ID of the ticket | | | text | string | yes | Text of the note | Hello world | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/tickets/addnote.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","ticket_id":"","text":"Hello world"}' ``` ### get_ticket_activity `POST https://api.datalyse.io/api/1.0/tickets/getactivity.json` Get activity/timeline for a ticket | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token | YOUR_APIKEY | | ticket_id | string | yes | ID of the ticket | | | type | string | no | Filter by activity type (optional) | | | var_resultspage | string | yes | Maximum number of results to display | 20 | | var_page | string | yes | Page number | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/tickets/getactivity.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","ticket_id":"","var_resultspage":"20","var_page":"1"}' ``` ## Uploads ### uploads_create `POST https://api.datalyse.io/api/1.0/uploads/create.json` Secure multipart upload for public API, AI assistants and MCP tools. Upload 1-5 files under the repeated 'files' field (10 MiB each, 25 MiB aggregate). Send the API key in the multipart field token, consistently with the other POST APIs. Files may optionally be attached to one visible CRM record by sending entitykey and record_id together; company and RBAC scope always come from the token, never from client-supplied IDs. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token sent as a multipart text field, using the same token value as the other APIs. Scoped app tokens need the 'uploads' scope; personal client tokens remain constrained by their user RBAC. Required. | YOUR_APIKEY | | files | file[] | yes | Repeat this multipart field once per local file. Required; 1-5 files, max 10 MiB each and 25 MiB combined. Allowed extensions/MIME pairs: pdf, jpg/jpeg, png, gif, webp, txt, md, csv, doc, xls, ppt, docx, xlsx, pptx, mp3, wav, ogg and mp4. Content signatures are checked; SVG/HTML/scripts/executables and application/octet-stream are rejected. | /path/to/document.pdf | | entitykey | string | no | Optional entity key from entities/schemas.json: leads, companies, opportunities, tasks, tickets or cust_*. Must be sent together with record_id. Omit both fields for a private, unassociated upload owned by the API user. | leads | | record_id | string | no | Optional 24-character ObjectId of the record to attach to. Must belong to the token company and be visible under the token user's RBAC scope; otherwise the API returns record_not_found_or_not_visible without revealing cross-company existence. | 69d9d868740030527e46055a | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/uploads/create.json' \ -F 'token=YOUR_APIKEY' \ -F 'files=@/path/to/document.pdf' ``` Errors: - HTTP 400 — `invalid_multipart \| invalid_content_length \| unexpected_query_parameter \| files_required \| unexpected_form_field \| unexpected_file_field \| incomplete_association \| invalid_entitykey \| invalid_record_id \| invalid_file_name \| invalid_temporary_file \| empty_file \| file_size_mismatch`: Malformed multipart input, missing files, unsupported fields, or invalid optional association. - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound`: The multipart token field is missing, invalid or no longer authorized. - HTTP 403 — `permission_denied \| scope_denied`: The token user role is not allowed to upload or a scoped app token lacks the 'uploads' scope. - HTTP 404 — `entity_not_found \| record_not_found_or_not_visible`: The optional entity/record does not exist in this company or is outside the token user's RBAC visibility. - HTTP 411 — `content_length_required`: Content-Length is required so the server can enforce the aggregate request limit before parsing. - HTTP 413 — `request_too_large \| file_too_large \| too_many_files \| aggregate_size_too_large`: Request exceeds 27 MiB, a file exceeds 10 MiB, more than 5 files were sent, or files exceed 25 MiB combined. - HTTP 415 — `multipart_form_data_required \| file_type_not_allowed \| mime_extension_mismatch \| file_signature_mismatch`: Wrong request media type or a file failed the extension, MIME or content-signature allowlist. - HTTP 500 — `upload_failed \| server_error \| record_visibility_check_failed`: Unexpected storage, database or visibility failure. Temporary files are cleaned and completed cloud/database writes are rolled back best-effort. Notes: - MCP CALL SHAPE: use POST multipart/form-data, send -F 'token=YOUR_APIKEY', and repeat -F 'files=@/local/path' for every file. Do not manually set the multipart Content-Type boundary; curl/your HTTP client must generate it. - To upload without attaching to a CRM record, omit BOTH entitykey and record_id. To attach, first discover entity keys with POST /api/1.0/entities/schemas.json and use a record id visible to this same token. - The server derives company_id and agent_id exclusively from the token field after the bounded multipart parser completes. Before parsing it enforces Content-Length and per-request size limits. It rejects company_id, agent_id, paths, URLs and every unrecognized form field. - Scoped app tokens must include the 'uploads' scope. Personal client tokens use the existing role and record-visibility RBAC model. - Client filenames are display metadata only: path components/control characters are removed and the storage key is generated server-side from cryptographic randomness plus a fresh ObjectId. Upload uses an S3 no-overwrite precondition. - The response intentionally contains no cloud path, public URL, company_id, agent_id, token or storage secret. Use the returned file id in subsequent authorized API/UI flows. - The existing /upload_fildatasnow and /upload_fildatasnowMULTIPLE routes remain disabled in the public/AI catalog and are not aliases of this endpoint. ## Workflows ### workflows_list `POST https://api.datalyse.io/api/1.0/workflows/list.json` List workflows of the authenticated company so AI/MCP can discover the Mongo `_id` needed by workflows_start and workflows_manualexecute. Filter by `name` with a partial case-insensitive contains match. Optional status filter (0=paused, 1=active). Returns a safe DTO with `_id`, name, status/workflow_status, fortype and queue counters — never the raw block graph. Paginate with perpage (max 5000) and var_page. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. The token user must be master or have manageworkflows='y'; scoped app tokens require the workflows scope. Required. | YOUR_APIKEY | | name | string | no | Optional partial case-insensitive filter on workflow name (contains). Preferred for AI/MCP. Example: 'follow-up' matches 'Qualified lead follow-up'. Max 200 characters. Legacy alternative: search_value with filter_typesworkflows='name'. | follow-up | | var_workflowsstatus | string | no | Optional status filter: 0 for paused, 1 for active. Omit or empty for all statuses. | | | search_value | string | no | Legacy search text used by the frontend socket. Prefer `name` from AI/MCP. When both are sent, `name` wins. Max 200 characters. | | | filter_typesworkflows | string | no | Legacy field selector for search_value. Only 'name' is allowed; omit when using the `name` parameter. | name | | perpage | number | no | Results per page, 1-5000 (default 20). Aliases: var_resultspage, page_size. | 20 | | var_resultspage | number | no | Legacy alias of perpage (frontend socket). | 20 | | var_page | number | no | Page number starting at 1 (default 1). Alias: page. | 1 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/workflows/list.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","name":"follow-up","perpage":50}' ``` Find workflows by name then activate: ```json { "token": "YOUR_APIKEY", "name": "follow-up", "perpage": 50 } ``` List only active workflows: ```json { "token": "YOUR_APIKEY", "var_workflowsstatus": "1", "perpage": 100 } ``` Errors: - HTTP 400 — `invalid_status \| invalid_filter_field \| search_too_long`: Status is not 0/1, filter field is not name, or the name/search text exceeds 200 characters. - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| client_id_not_found \| conflicting_token \| token_type_not_allowed \| client_disabled \| company_not_found \| token_company_mismatch`: The API token or its tenant binding is invalid. - HTTP 403 — `permission_denied \| scope_denied`: The token user is neither master nor allowed to manage workflows, or the app token lacks workflows scope. - HTTP 500 — `internal_error`: Unexpected persistence or query failure. Notes: - Typical AI flow: POST list.json with name → read workflows[]._id → POST start.json with workflow_id → optionally POST manualexecute.json. - name uses a MongoDB case-insensitive contains regex (special characters escaped). Empty name returns all company workflows (subject to status/pagination). - The DTO is intentionally redacted: no blocks, conditions, webhooks or action payloads. - status/workflow_status 0 = paused, 1 = active. fortype/workflow_fortype is the entity type (leads, companies, opportunities, tickets, calendar, budgets, events). - Socket parity: workflows_getWorkflows still uses search_value + filter_typesworkflows and a 500-row socket cap; HTTP allows perpage up to 5000. ### workflows_create `POST https://api.datalyse.io/api/1.0/workflows/create.json` Create a paused workflow. Legacy mode omits definition and creates only the workflow header. Complete versioned mode sends definition.version=1 and atomically creates the header plus a fully validated block graph. Complete mode requires idempotency_key, generates every Mongo ObjectId server-side, enforces tenant/plan boundaries and returns the temporary-key-to-ObjectId map. Both modes always persist workflow status 0. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. The token user must be master or have manageworkflows='y'; scoped app tokens require the workflows scope. Required. | YOUR_APIKEY | | fortype | string | yes | Workflow entity type: leads, companies, opportunities, tickets, calendar, budgets or events. Required. | leads | | name | string | no | Human-readable workflow name, 1-200 characters; HTML/control characters are rejected. Defaults to New workflow for . (optional) | AI follow-up workflow | | idempotency_key | string | no | Stable 8-128 character retry key using letters, digits, dot, underscore, colon or hyphen. Required in complete mode; optional in legacy mode. Same key and normalized payload replays with HTTP 200; a different payload returns HTTP 409. | workflow:create:customer-followup:v1 | | definition | object | no | Optional versioned complete graph. Omit for legacy header-only creation. With definition present, version must be 1 and idempotency_key is mandatory. | | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/workflows/create.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","fortype":"leads","name":"Qualified lead follow-up","idempotency_key":"workflow:create:qualified-followup:v1","definition":{"version":1,"activator":{"conditions":[[{"fortype":"leads","identifier":"status","op":"eq","text":"qualified"}]]},"entry":"b:wait","blocks":[{"key":"b:wait","block_type":"delay","next":"b:segment","payload":{"minutes":5}},{"key":"b:segment","block_type":"bifurcation","payload":{"conditions":[[{"fortype":"leads","identifier":"leadtypedatal","op":"eq","text":"vip"}],[{"fortype":"leads","identifier":"leadtypedatal","op":"ne","text":"vip"}]],"conditions_next":["b:email-vip","b:notify-owner"]}},{"key":"b:email-vip","block_type":"sendemail","payload":{"to":"client","subject":"Your account is ready","text":"

We will contact you shortly.

","assignment":"current","tracking":true}},{"key":"b:notify-owner","block_type":"sendnoti","payload":{"assignment":"current","title":"Qualified lead","text":"A qualified lead requires follow-up.","send_notification":true}}]}}' ``` Complete v1 contact workflow with a branch: ```json { "token": "YOUR_APIKEY", "fortype": "leads", "name": "Qualified lead follow-up", "idempotency_key": "workflow:create:qualified-followup:v1", "definition": { "version": 1, "activator": { "conditions": [ [ { "fortype": "leads", "identifier": "status", "op": "eq", "text": "qualified" } ] ] }, "entry": "b:wait", "blocks": [ { "key": "b:wait", "block_type": "delay", "next": "b:segment", "payload": { "minutes": 5 } }, { "key": "b:segment", "block_type": "bifurcation", "payload": { "conditions": [ [ { "fortype": "leads", "identifier": "leadtypedatal", "op": "eq", "text": "vip" } ], [ { "fortype": "leads", "identifier": "leadtypedatal", "op": "ne", "text": "vip" } ] ], "conditions_next": [ "b:email-vip", "b:notify-owner" ] } }, { "key": "b:email-vip", "block_type": "sendemail", "payload": { "to": "client", "subject": "Your account is ready", "text": "

We will contact you shortly.

", "assignment": "current", "tracking": true } }, { "key": "b:notify-owner", "block_type": "sendnoti", "payload": { "assignment": "current", "title": "Qualified lead", "text": "A qualified lead requires follow-up.", "send_notification": true } } ] } } ``` Legacy header-only workflow: ```json { "token": "YOUR_APIKEY", "fortype": "opportunities", "name": "Opportunity won follow-up" } ``` Errors: - HTTP 400 — `invalid_fortype \| invalid_name \| idempotency_key_required \| invalid_idempotency_key \| unexpected_field \| client_supplied_id`: Invalid common fields, unsupported fortype, missing complete-mode retry key, unknown input, or an attempt to supply server-owned IDs. - HTTP 400 — `invalid_definition \| unsupported_definition_version \| invalid_activator \| activator_requires_conditions_or_event \| workflow_event_requires_events_fortype \| events_fortype_requires_workflow_event \| invalid_workflow_event \| invalid_conditions \| invalid_condition_group \| invalid_condition \| invalid_condition_fortype \| invalid_condition_field \| invalid_condition_operator \| invalid_condition_value`: definition.version/activator/condition structure or values are invalid. - HTTP 400 — `invalid_block \| invalid_block_type \| invalid_block_payload \| invalid_json_value \| invalid_object_key \| payload_field_required \| invalid_payload_field \| payload_field_too_large \| conflicting_payload_fields \| invalid_reference_id \| duplicate_reference_id \| assignment_targets_required \| invalid_whatsapp_template_json \| webhook_https_required \| invalid_webhook_url \| webhook_private_destination \| webhook_host_not_resolvable`: A block payload failed its type-specific allowlist, tenant-reference format, size, assignment, JSON, or public HTTPS webhook validation. - HTTP 400 — `invalid_block_reference \| duplicate_block_key \| workflow_graph_missing_entry \| workflow_graph_missing_reference \| workflow_graph_cycle \| workflow_graph_orphan_blocks \| workflow_graph_multiple_parents \| workflow_graph_entry_has_parent \| invalid_branch_references \| branch_reference_count_mismatch`: Temporary b:* references do not form one complete rooted acyclic graph. - HTTP 400 — `reference_not_found_or_cross_tenant`: An agent, department or queue reference is absent from the token company; cross-company existence is not disclosed. - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| client_id_not_found \| conflicting_token \| token_type_not_allowed \| client_disabled \| company_not_found \| token_company_mismatch`: Missing or invalid API token - HTTP 403 — `permission_denied \| scope_denied \| plan_restricted`: Token user lacks manageworkflows/workflows scope, workflows are disabled by plan, or a multi-object graph is not allowed. Plan lookup fails closed for multi-object workflows. - HTTP 409 — `workflow_limit_reached \| idempotency_conflict`: Company reached 500 workflows, or the same idempotency_key was reused with a different normalized payload. - HTTP 413 — `workflow_definition_too_large \| workflow_block_too_large \| workflow_graph_too_large`: Definition exceeds 512 KiB, one payload exceeds 32 KiB, or the graph exceeds 200 blocks. - HTTP 500 — `internal_error \| insert_failed \| transaction_not_committed \| idempotency_replay_corrupt`: Unexpected persistence failure or an incomplete historical idempotency record. Notes: - AI SAFETY: creation never activates or executes a workflow. The persisted workflow status and response workflow_status are always 0. - Legacy compatibility: omit definition to retain header-only creation; legacy idempotency_key remains optional. - Complete mode: definition.version=1 and idempotency_key are mandatory. Header and every block are inserted in one majority MongoDB transaction. - Idempotency compares a SHA-256 hash of the normalized fortype, requested name and complete definition. Identical replay is HTTP 200; changed payload is HTTP 409. - All graph IDs are generated server-side. Clients only use temporary keys matching b:; raw workflow/block IDs in the request are rejected. - The graph must contain 1-200 reachable blocks, exactly one root at definition.entry, no missing references, cycles, orphan blocks or nodes with multiple incoming edges. - Bifurcation payload.conditions_next has exactly one temporary target per conditions OR-group. - Agent, department and queue ObjectIds are accepted only in their documented payload fields and must belong to the authenticated company. - Multi-object conditions/property actions require allow_multiobject_workflows; missing plan data denies multi-object creation. - Webhook URLs must use HTTPS, contain no credentials, resolve successfully and resolve only to public non-loopback addresses. - The live workflows_editandcreate_block socket and this endpoint use the same exact Mongo field normalizer. ### workflows_start `POST https://api.datalyse.io/api/1.0/workflows/start.json` Activate an existing workflow after validating its complete tenant-owned graph, connected action paths, branches and stored trigger or conditions. Repeating the request when the workflow is already active is safe and returns an idempotent HTTP 200 response. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. The token user must be master or have manageworkflows='y'; scoped app tokens require the workflows scope. Required. | YOUR_APIKEY | | workflow_id | string | yes | 24-character Mongo ObjectId of the workflow to activate. It must belong to the authenticated company. Required. | 69d9d868740030527e460551 | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/workflows/start.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","workflow_id":"69d9d868740030527e460551"}' ``` Activate a validated workflow: ```json { "token": "YOUR_APIKEY", "workflow_id": "69d9d868740030527e460551" } ``` Errors: - HTTP 400 — `invalid_workflow_id \| invalid_workflow_status`: The workflow identifier or requested status is malformed. - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| client_id_not_found \| conflicting_token \| token_type_not_allowed \| client_disabled \| company_not_found \| token_company_mismatch`: The API token or its tenant binding is invalid. - HTTP 403 — `permission_denied \| scope_denied`: The token user is neither master nor allowed to manage workflows, or the app token lacks workflows scope. - HTTP 404 — `workflow_not_found`: No workflow with this ID exists in the authenticated company. - HTTP 413 — `workflow_graph_too_large`: The stored graph exceeds the 200-block validation limit. - HTTP 422 — `workflow_actions_required \| workflow_graph_foreign_or_missing_block \| workflow_graph_cycle \| workflow_graph_too_many_branches \| workflow_graph_orphan_blocks \| workflow_graph_multiple_parents \| workflow_graph_incomplete_branches \| workflow_action_path_incomplete \| invalid_workflow_block_type \| invalid_workflow_trigger \| invalid_workflow_conditions \| workflow_trigger_index_invalid`: Activation was refused because the complete stored graph, action paths, branches, trigger or conditions are not executable. - HTTP 500 — `internal_error`: Unexpected persistence failure; workflow status was not intentionally changed. Notes: - The shared core checks tenant ownership and traverses the complete workflows_criticalGraph before writing status=1. - Every block must be reachable from workflow.next, references must stay inside the workflow, and every terminal path must contain a real action. - Condition triggers are normalized with the stored-condition allowlist and must have a consistent conditions_array index; event triggers must be supported. - An already active valid workflow returns changed=false and idempotent=true without a second state transition. - This endpoint activates only; it does not manually enroll existing records. Use workflows_manualexecute for that. ### workflows_manualexecute `POST https://api.datalyse.io/api/1.0/workflows/manualexecute.json` Enroll existing records that match an active workflow's stored conditions into its manual execution queue. The operation is tenant-scoped, plan-gated, limited to 5,000 records and transactionally idempotent. Omitted or empty scheduling options mean run now in UTC: 00:00-23:59, every day, up to 100 records per minute. | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | token | string | yes | API access token. The token user must be master or have manageworkflows='y'; scoped app tokens require the workflows scope. Required. | YOUR_APIKEY | | workflow_id | string | yes | 24-character Mongo ObjectId of an active workflow owned by the authenticated company. Required. | 69d9d868740030527e460551 | | from | string | yes | Primary record source. Must exactly match workflow_fortype: leads, companies, opportunities, calendar or budgets. Required. | leads | | idempotency_key | string | yes | Stable 8-128 character retry key using letters, digits, dot, underscore, colon or hyphen. Required for HTTP. The same key and normalized parameters replay with HTTP 200; changed parameters return HTTP 409. | workflow:manual:qualified-leads:2026-07-26 | | starttime | string | no | UTC execution-window start in HH:MM. Optional; omitted or empty defaults to 00:00. | 00:00 | | endtime | string | no | UTC execution-window end in HH:MM and not earlier than starttime. Optional; omitted or empty defaults to 23:59. | 23:59 | | maxperminute | integer | no | Queue records processed per minute, from 1 to 1000. Optional; omitted or empty defaults to 100, allowing the 5,000-record maximum to drain in about 50 minutes. | 100 | | execution_days | array | no | Non-empty array of lowercase UTC weekday names. Optional; omitted or empty-string legacy input defaults to all seven days. | sunday,monday,tuesday,wednesday,thursday,friday,saturday | Example: ```bash curl -X POST 'https://api.datalyse.io/api/1.0/workflows/manualexecute.json' \ -H 'Content-Type: application/json' \ -d '{"token":"YOUR_APIKEY","workflow_id":"69d9d868740030527e460551","from":"leads","idempotency_key":"workflow:manual:qualified-leads:2026-07-26"}' ``` Enroll matching leads for execution now: ```json { "token": "YOUR_APIKEY", "workflow_id": "69d9d868740030527e460551", "from": "leads", "idempotency_key": "workflow:manual:qualified-leads:2026-07-26" } ``` Errors: - HTTP 400 — `invalid_workflow_id \| invalid_workflow_source \| idempotency_key_required \| invalid_idempotency_key \| conflicting_idempotency_key \| invalid_starttime \| invalid_endtime \| invalid_execution_window \| invalid_execution_days \| invalid_maxperminute`: A required identifier, source, retry key or UTC scheduling option is missing or malformed. - HTTP 401 — `EMPTY_TOKEN \| error_apitokennotfound \| client_id_not_found \| conflicting_token \| token_type_not_allowed \| client_disabled \| company_not_found \| token_company_mismatch`: The API token or its tenant binding is invalid. - HTTP 403 — `permission_denied \| scope_denied \| plan_restricted`: The actor lacks workflow-management permission/scope, or the company plan does not enable allow_manual_workflow. - HTTP 404 — `workflow_not_found`: No workflow with this ID exists in the authenticated company. - HTTP 409 — `workflow_not_active \| bulk_in_progress \| idempotency_conflict`: The workflow must first be active, another queue is already active for it, or this idempotency key was reused with different normalized parameters. - HTTP 413 — `bulk_limit_exceeded \| workflow_graph_too_large`: More than 5,000 records match, or the stored graph exceeds 200 blocks. - HTTP 422 — `workflow_source_mismatch \| workflow_conditions_not_applicable \| invalid_workflow_conditions \| workflow_actions_required \| workflow_graph_foreign_or_missing_block \| workflow_graph_cycle \| workflow_graph_too_many_branches \| workflow_graph_orphan_blocks \| workflow_graph_multiple_parents \| workflow_graph_incomplete_branches \| workflow_action_path_incomplete \| invalid_workflow_block_type \| invalid_workflow_trigger \| workflow_trigger_index_invalid`: The stored source, conditions, trigger or complete action graph cannot safely drive a manual execution. - HTTP 503 — `queue_safety_unavailable`: Required unique/TTL queue indexes could not be verified, so no queue was created. - HTTP 500 — `queue_commit_failed \| internal_error`: The transactional job/queue write failed; a retry with the same idempotency key is safe. Notes: - HTTP requires idempotency_key in the JSON body or Idempotency-Key header. If both are supplied, they must match. - Idempotency hashes workflow_id, from and the normalized schedule. Equal retries return HTTP 200; the first committed queue returns HTTP 201. - The job record and all queue rows are inserted in one majority MongoDB transaction. Unique indexes allow one active queue per workflow and one row per job/record. - Only stored, allowlisted workflow conditions are compiled into MongoDB predicates; request callers cannot supply condition fields or operators. - The workflow must be active, fully executable and have workflow_fortype exactly equal to from. - At most 5,000 records are queued. An existing legacy or transactional queue for the workflow causes HTTP 409. - Run-now defaults are UTC 00:00-23:59, all seven days and maxperminute=100. Empty legacy strings normalize to those defaults instead of blocking forever. - Queue workers claim rows with a lease before execution. Active queue rows are not TTL-deleted; terminal idempotency job records are retained for 30 days. - The HTTP request enqueues matching records; execution is asynchronous and no production workflow is run by the API response itself.