Public API for integrating CalcHub with your own tools, dashboards and automations. Base URL: https://calchub.echo-digital.es
Every request to /api/v1/* requires a personal API key. Generate one at Dashboard → API Keys. The full token is shown once — copy it straight away. Pass it as a standard Bearer token:
Authorization: Bearer calk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keep keys out of client-side JavaScript and mobile apps. Revoke a compromised key from the dashboard; any integration using it will stop working within seconds.
60 requests per minute per API key. Exceeded requests get HTTP 429. If you need higher throughput, open multiple keys per integration and spread the traffic.
All errors return JSON of the shape { "error": "..." } with an appropriate HTTP status:
400 — bad request payload401 — missing / invalid / revoked token403 — resource not owned by the authenticated user404 — resource not found429 — rate limit or abuse limit exceeded500 — server-side failure, report to supportList endpoints use keyset cursors. The response shape is:
{
"items": [ ... ],
"nextCursor": "cmo...", // pass as ?cursor= on the next call
"hasMore": true
}?take= defaults to 50, max 200.
/api/v1/calculatorsAll calculators assigned to the authenticated user. Supports cursor pagination (?take=, ?cursor=).
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/calculators?take=50"
{
"items": [
{ "id": "cmo...", "name": "Recuperator Calculator", "slug": "recuperator", "embedToken": "cmo...", "allowedDomains": ["mycond.eu"], "isActive": true, "createdAt": "2026-04-21T..." }
],
"nextCursor": null,
"hasMore": false
}/api/v1/calculators/:idFull calculator including definition JSON, linked bot id, allowed domains and embed token.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/calculators/cmo..."
/api/v1/calculatorsCreates a calculator from a CalculatorDefinition JSON and auto-assigns it to you, returning the embed token.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Recuperator savings",
"slug": "recuperator-savings",
"category": "HVAC",
"definition": { "elements": [ ... ], "settings": { ... } },
"allowedDomains": ["mycond.eu"]
}' "https://calchub.echo-digital.es/api/v1/calculators"HTTP 201
{ "id": "cmo...", "name": "Recuperator savings", "slug": "recuperator-savings", "embedToken": "cmo...", "allowedDomains": ["mycond.eu"] }/api/v1/calculators/:idUse PUT. Patches any of: name, description, category, isActive, botId, definition.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "isActive": false }' "https://calchub.echo-digital.es/api/v1/calculators/cmo..."/api/v1/calculators/:idCascades: removes the UserCalculator assignment and the Calculator itself. Leads and stats are preserved.
curl -X DELETE -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/calculators/cmo..."
/api/v1/calculators/:id/domainsReplaces the allowedDomains whitelist for this calculator. Empty array = any origin. Subdomain match ('mycond.eu' allows 'shop.mycond.eu').
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "allowedDomains": ["mycond.eu", "partner-site.com"] }' \
"https://calchub.echo-digital.es/api/v1/calculators/cmo.../domains"/api/v1/calculators/generateTakes a natural-language prompt, runs Gemini (your saved key or the server fallback) and returns a CalculatorDefinition + rendered HTML. Does NOT persist — pipe into POST /v1/calculators to save.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "prompt": "Calculator for home heating savings with electricity price in UAH" }' \
"https://calchub.echo-digital.es/api/v1/calculators/generate"{ "definition": { "elements": [...], "settings": {...} }, "html": "<form>...</form>" }/api/v1/leadsNewest first. Filters: ?calculatorId=, ?synced=true|false. Cursor pagination via ?cursor=.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/leads?take=50&synced=false"
{
"items": [
{ "id": "cmo...", "contactName": "John", "email": "j@ex.com", "phone": "+380...", "calculatorData": { ... }, "odooSyncStatus": { "synced": true, "recordIds": [{ "model": "res.partner", "id": 79647, "action": "linked" }], "attemptedAt": "2026-04-21T..." }, "createdAt": "..." }
],
"nextCursor": "...",
"hasMore": true
}/api/v1/leads/:idReturns the full Lead row including calculatorData and odooSyncStatus.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/leads/cmo..."
/api/v1/leadsServer-to-server lead creation. Skips honeypot / time-gate / Turnstile (they're only meaningful for browser submissions) but still enforces your abuse limits (per-phone/email/day, E.164).
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{
"calculatorId": "cmo...",
"contactName": "John",
"email": "j@ex.com",
"phone": "+380...",
"preferredChannel": "email",
"data": { "area": 120, "city": "Kyiv" }
}' \
"https://calchub.echo-digital.es/api/v1/leads"HTTP 201
{ "id": "cmo...", "userId": "...", "calculatorId": "cmo...", ... }/api/v1/leads/:id/syncForces an immediate (re-)sync of this lead to Odoo. Useful after fixing a broken mapping or Odoo credentials.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/leads/cmo.../sync"
/api/v1/leads/retry-failedBulk re-sync for up to 500 leads where the last Odoo push failed. Returns {attempted, ok, fail}.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/leads/retry-failed"
/api/v1/leads/:idRemoves the lead. Conversations referencing it (via leadId) stay but the relation becomes dangling — only use for GDPR or test-data cleanup.
curl -X DELETE -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/leads/cmo..."
/api/v1/leads?format=csvReturns text/csv with all matching leads (up to 10 000) for the given filters. Headers: id, createdAt, calculatorId, contactName, email, phone, telegramNick, preferredChannel, calculatorData, odooSynced.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/leads?format=csv" -o leads.csv
/api/v1/conversationsFilters: ?channel=telegram|email|whatsapp, ?status=active|escalated|takeover|closed, ?botId=. Cursor pagination.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/conversations?status=escalated"
/api/v1/conversations/:idReturns the lead, the linked bot, summary fields and the latest N messages (default 50). Load older with ?before=<messageId>.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/conversations/cmo..."
/api/v1/conversations/:id/messagesHuman operator sends a message into the conversation through the bot's channel. Sets status=takeover so the AI stops auto-replying.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "content": "Hi, this is the manager!" }' \
"https://calchub.echo-digital.es/api/v1/conversations/cmo.../messages"HTTP 201
{ "id": "cmo...", "role": "takeover", "content": "...", "createdAt": "..." }/api/v1/conversations/:idTransition status between active / closed / takeover / escalated. Takeover pauses AI auto-reply.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "status": "closed" }' \
"https://calchub.echo-digital.es/api/v1/conversations/cmo..."/api/v1/conversations/:id/summaryRuns the bot's AI over the conversation and saves a short Request / Key facts / Status / Next step summary on the Conversation.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/conversations/cmo.../summary"
{ "summary": "Request: ...\nKey facts:\n- ...", "summaryAt": "...", "summaryMessageCount": 18 }/api/v1/botsAll bots you own, with channel config summaries and conversation counts.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/bots"
/api/v1/bots/:idFull bot settings including prompt, model, reply timing, working hours.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/bots/cmo..."
/api/v1/botsEnforces the same rules as the dashboard: name + prompt required, required AI key for the chosen model, SMTP fromEmail must match SMTP user domain, Telegram webhook auto-registered with a per-bot secret.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Sales bot",
"prompt": "You are a friendly sales assistant for ...",
"model": "models/gemini-flash-latest",
"responseLanguage": "Ukrainian",
"replyDelayMin": 10,
"replyDelayMax": 15,
"typingIndicator": true
}' "https://calchub.echo-digital.es/api/v1/bots"/api/v1/bots/:idPatch any subset of bot fields. Changing the model across providers still requires the matching API key.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "isActive": false }' "https://calchub.echo-digital.es/api/v1/bots/cmo..."/api/v1/bots/:idUnlinks calculators from the bot and removes the bot. Conversations and leads stay.
curl -X DELETE -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/bots/cmo..."
/api/v1/statsTotals plus daily impressions / interactions / submissions for the last N days (?days=30, max 365). Aggregates across every calculator you own.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/stats?days=30"
{
"totals": { "leads": 128, "conversations": 94, "impressions": 5210, "interactions": 1840, "submissions": 132 },
"daily": [
{ "date": "2026-04-21", "impressions": 210, "interactions": 74, "submissions": 6 }
]
}/api/v1/stats/calculators/:idDaily impressions / interactions / submissions for one of your calculators, last N days (?days=30, max 365).
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/stats/calculators/cmo...?days=90"
/api/v1/campaignsAll active / paused campaigns you own. Archived campaigns are excluded.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns"
/api/v1/campaigns/:idFull campaign config + the 20 most recent runs with their counters.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo..."
/api/v1/campaignsbotId must belong to you. audience filter supports calculatorIds, channels, conversationStatuses, inactivityDays, createdAfter, createdBefore. runIntervalDays=null for manual-only; any positive number enables the scheduler.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Weekly nudge",
"botId": "cmo...",
"reminderPrompt": "Remind the customer politely to book a consultation.",
"audience": { "channels": ["whatsapp"], "inactivityDays": 7 },
"runIntervalDays": 7,
"msgPerMinute": 20,
"maxPerRun": 500
}' "https://calchub.echo-digital.es/api/v1/campaigns"HTTP 201
{ "id": "cmo...", "status": "active", ... }/api/v1/campaigns/:idPatch any of name, reminderPrompt, audience, runIntervalDays, msgPerMinute, maxPerRun. 409 if a run is in progress — pause first.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "runIntervalDays": 14 }' "https://calchub.echo-digital.es/api/v1/campaigns/cmo..."/api/v1/campaigns/:idSoft-delete: campaign stops running but history (runs / messages) is preserved for audit.
curl -X DELETE -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo..."
/api/v1/campaigns/:id/runTriggers a run immediately. Returns 202 Accepted — the worker finishes in the background. Same atomic claim as the scheduler, so a parallel scheduled run wins the race and the API returns 409.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo.../run"
/api/v1/campaigns/:id/pauseScheduler stops triggering this campaign. A run already in progress is NOT aborted — wait for it to finish, then pause takes effect.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo.../pause"
/api/v1/campaigns/:id/resumeFlips status back from paused to active.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo.../resume"
/api/v1/campaigns/audience/previewGiven a draft filter, returns count + 10-row sample without persisting. Use before saving so you can see how many leads the campaign would touch.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "botId": "cmo...", "audience": { "inactivityDays": 7 } }' \
"https://calchub.echo-digital.es/api/v1/campaigns/audience/preview"{ "count": 47, "sample": [ { "id": "cmo...", "contactName": "Bebra", "email": "..." }, ... ] }/api/v1/campaigns/:id/runsAll runs of this campaign, newest first. Cursor pagination.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo.../runs"
/api/v1/campaigns/:id/runs/:runIdRun metadata + its messages (pending / sent / failed / skipped with the AI-generated content actually delivered).
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/campaigns/cmo.../runs/cmo..."
/api/v1/errorsRecent BotError entries scoped to your bots. Filters: ?botId=, ?type=, ?conversationId=, ?days= (1-90). Cursor pagination.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/errors?days=7&type=ai"
/api/v1/ai-modelsReturns the Gemini and Anthropic models your saved API keys can actually call. Empty array for a provider = no key saved.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/ai-models"
/api/v1/ai-models/testPings every text-gen Gemini model your key sees and reports which actually respond (free-tier limits often reject some). Does NOT probe Claude — Anthropic has no cheap health ping.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/ai-models/test"
{ "results": [ { "name": "models/gemini-2.5-flash", "displayName": "Gemini 2.5 Flash", "ok": true, "status": 200 }, ... ] }/api/v1/test/telegramCalls Telegram's getMe so you can validate a token before saving. Body: { token }.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "token": "123456:ABC..." }' "https://calchub.echo-digital.es/api/v1/test/telegram"/api/v1/test/smtpAttempts to open an SMTP connection and authenticate. Body: full SmtpConfig (host, port, secure, user, pass, fromEmail, fromName).
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "host": "smtp.gmail.com", "port": 587, "user": "you@gmail.com", "pass": "..." }' \
"https://calchub.echo-digital.es/api/v1/test/smtp"/api/v1/test/whatsappPings the provider (Wasender or Meta) to confirm credentials and return the connected phone. Body: WhatsAppConfig.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "provider": "wassenger", "wassengerToken": "..." }' \
"https://calchub.echo-digital.es/api/v1/test/whatsapp"/api/v1/test/odooDry-run authenticate against Odoo's JSON-RPC. Returns { ok, uid, error }. Body: { url, db, username, apiKey }.
curl -X POST -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "url": "https://stage.example.com", "db": "erp_stage", "username": "you@ex.com", "apiKey": "..." }' \
"https://calchub.echo-digital.es/api/v1/test/odoo"/api/v1/profileBasic account info (id, email, name, role).
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/profile"
/api/v1/profileOnly updatable field via the API is `name`. Password resets and email changes stay dashboard-only.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "name": "Luka H." }' "https://calchub.echo-digital.es/api/v1/profile"/api/v1/settings/abuse-limitsCurrent per-phone/email/IP daily caps + E.164 enforcement toggle. Also returns the platform defaults.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/abuse-limits"
/api/v1/settings/abuse-limitsAccepts phonePerDay, emailPerDay, ipPerHour, requireE164. Server clamps numerical values (0..100 / 0..1000). 0 disables a check.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "phonePerDay": 5, "emailPerDay": 5, "ipPerHour": 20, "requireE164": true }' \
"https://calchub.echo-digital.es/api/v1/settings/abuse-limits"/api/v1/settings/odooConnection metadata (url/db/username/key preview) plus current log-note + escalation notification settings. Never returns the raw Odoo API key.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo"
/api/v1/settings/odooPatch log-note template and escalation settings (enable flag, template, partnerIds to @-mention). Reconnecting Odoo (url/db/user/apiKey) stays dashboard-only.
curl -X PATCH -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "escalationNotifyEnabled": true, "escalationNotifyPartnerIds": [79647] }' \
"https://calchub.echo-digital.es/api/v1/settings/odoo"/api/v1/settings/odoo/usersActive non-share Odoo users you can @-mention on escalation. Use the returned partnerId values in escalationNotifyPartnerIds above.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo/users"
/api/v1/settings/odoo/modelsAll ir.model entries visible to your Odoo user. Cache 10 min.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo/models"
/api/v1/settings/odoo/fields?model=res.partnerFor building mapping UI. ?model= is the technical Odoo name (e.g. res.partner, crm.lead).
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo/fields?model=res.partner"
/api/v1/settings/odoo/auto-syncReturns intervalMinutes (0 = off, 15, 60, 360) and lastAutoSyncAt.
curl -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo/auto-sync"
/api/v1/settings/odoo/auto-syncAllowed values: 0, 15, 60, 360 (minutes). A background worker retries failed leads at this cadence.
curl -X PUT -H "Authorization: Bearer calk_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{ "intervalMinutes": 60 }' "https://calchub.echo-digital.es/api/v1/settings/odoo/auto-sync"/api/v1/settings/odooWipes saved Odoo credentials and clears the in-memory schema cache. Bots keep running, but future leads won't sync.
curl -X DELETE -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo"
/api/v1/settings/odoo/cacheDrops the cached models/fields (10 min TTL). Use after someone changes the Odoo schema and you want CalcHub to pick up new fields immediately.
curl -X DELETE -H "Authorization: Bearer calk_YOUR_TOKEN" "https://calchub.echo-digital.es/api/v1/settings/odoo/cache"