Skip to Content
Builder Docs
CRM, APIs & Knowledge Bases

Custom HTTP Tools & Webhooks

Build reliable mid-call REST integrations (`http_api`), store API keys in the Credentials Vault, use `body_template` & double-brace `{{param}}` substitution, test requests with header masking, and configure Post-Call Webhooks.

Custom HTTP Tools & Webhooks

When your agent needs live data during a conversation—booking a slot in Google Calendar, looking up an order in Shopify, or creating a lead in HubSpot/Zoho—you attach Custom HTTP Tools (http_api) to your Agent nodes. For guaranteed post-call CRM syncing, you add a Post-Call Webhook (webhook) node to the canvas.


Step 1: Store Secrets in the Credentials Vault

Never paste API tokens or Basic Auth passwords directly into raw tool headers. Store them once per organization in the Credentials Vault (/api/v1/credentials) and reference them by credential_uuid. (Full CRUD: GET/POST /api/v1/credentials/ and GET/PUT/DELETE /api/v1/credentials/{credential_uuid}; duplicate org name returns 409.)

Supported Credential Types

credential_typeRequired credential_data KeysOutbound Header Injected
none{} (must be empty)None
api_keyheader_name, api_key{header_name: api_key} (defaults header name to X-API-Key at runtime)
bearer_tokentokenAuthorization: Bearer <token>
basic_authusername, passwordAuthorization: Basic <base64>
custom_headerheader_name, header_value{header_name: header_value} (defaults header name to X-Custom at runtime)
oauth2_client_credentialsclient_id, client_secret, token_url (optional: scope, audience)Authorization: Bearer <cached_token>

Security & OAuth2 SSRF / Caching Behavior

  • Write-only API boundary: Reading or listing credentials never returns credential_data—secrets cannot be read back through the API once saved.
  • OAuth2 SSRF & caching: oauth2_client_credentials requires an https:// token_url with SSRF protection blocking private/loopback/internal hosts (10s token fetch timeout, requiring 200, token_type: bearer, access_token). Tokens cache in Redis with a 60-second pre-expiry buffer (short tokens with expires_in <= 60 bypass caching) and auto-invalidate on credential update/delete.
  • 401 auto-retry behavior: Pre-call fetch, Post-Call Webhook deliveries, dynamic transfer resolvers, and remote MCP sessions automatically invalidate the cached OAuth2 token and retry once on HTTP 401. Mid-call http_api tool calls (and tool test runs) do NOT auto-retry on 401—they return the status directly.

Interactive Credentials API

GET
/api/v1/credentials/

Authorization

headerAuthorizationBearer <token>

Authenticated builder/user JWT or API key for dashboard-scoped management endpoints.

Response Body

Credential metadata list

Example Request
curl -X GET 'https://api.talkar.in/api/v1/credentials/'
Example Response200
Empty
POST
/api/v1/credentials/

Authorization

headerAuthorizationBearer <token>

Authenticated builder/user JWT or API key for dashboard-scoped management endpoints.

Request Body

application/json
  1. body
name*string
description?string
credential_type*string
Value in"none""api_key""bearer_token""basic_auth""oauth2_client_credentials""custom_header"
credential_data*

Response Body

Created credential metadata

Example Request
curl -X POST 'https://api.talkar.in/api/v1/credentials/' \  -H 'Content-Type: application/json' \  -d '{  "name": "string",  "credential_type": "none",  "credential_data": {}}'
Example Response
Empty

Step 2: Configure Tools (http_api & All 6 Categories)

Organizations manage typed tools via /api/v1/tools across 6 categories: http_api, end_call (endCallReason: true injects a required reason parameter for the LLM), transfer_call, calculator, wait, and mcp.

Core http_api Configuration Fields

  • method: GET, POST, PUT, PATCH, or DELETE
  • url: Destination endpoint. Supports double-brace path placeholders like `https://api.example.com/orders/{{order_id}}` (single {order_id} is sent literally!).
  • credential_uuid: Reference to your stored Vault credential
  • headers: Static custom headers (Vault credential headers overwrite any colliding keys in headers)
  • timeout_ms: Request timeout in milliseconds (>= 1, default 5000 ms)
  • customMessage & customMessageType: Optional spoken filler (text or pre-recorded audio via customMessageRecordingId) played while waiting on slow APIs.

Parameter Modes: parameters vs preset_parameters

  1. LLM Parameters (parameters): Exposed in the LLM function schema. Each parameter defines a name (regex ^[a-zA-Z0-9_\-]+$, no spaces/dots), type (string, number, boolean, object, or array — note integer is NOT valid), description, and required flag.
  2. Preset Parameters (preset_parameters): Hidden from the LLM and configured with a value_template supporting `{{initial_context.*}}`, `{{gathered_context.*}}`, and flat initial context keys, coerced to declared types. At live runtime, preset_parameters take precedence over LLM parameters if both share a name.
  3. Reserved Names: Names cannot collide with reserved system prefixes (initial_context, gathered_context, current_time*, current_weekday*).

How Parameters Map to the Outbound Request

  • Path substitution (`{{param}}` in URL): Matches `{{param}}` in url, URL-encodes the value, and strips consumed path params from query params / default JSON bodies on live execution (while keeping them available for body_template interpolation).
  • GET & DELETE requests: Remaining parameters become URL query parameters (object/array values JSON-stringified); body_template is disallowed.
  • POST, PUT, PATCH requests:
    • Without body_template: remaining parameters serialize as a flat JSON object.
    • With body_template (must be a JSON object dict, max depth 20, max size 64 KB / 65,536 bytes): interpolates `{{param}}` placeholders anywhere in the nested structure ({"properties": {"email": "{{email}}", "qty": "{{qty}}"}}). Using `"{{qty}}"` as the entire string value preserves native JSON types (number/boolean/object/array), whereas `"Ref-{{id}}"` coerces to a string. Undeclared placeholders or duplicate rendered keys are rejected on save.

Step 3: Test & Debug Live With POST /api/v1/tools/{tool_uuid}/test

Run the tool test endpoint before putting an agent on a live phone line. It accepts { "llm_params": {...}, "preset_params": {...} } and returns:

  • Rendered request_method, request_url, request_params, request_body, and consumed_path_params (note: in test previews, request_params retains consumed path params and gives llm_params precedence, whereas live calls strip path params and prefer preset_parameters)
  • Masked request_headers (Vault credentials show only the last 4 characters)
  • Round-trip duration_ms, HTTP status_code, and Diagnostic Error Hints on >= 400 (400–5xx)
POST
/api/v1/tools/{tool_uuid}/test

Authorization

headerAuthorizationBearer <token>

Authenticated builder/user JWT or API key for dashboard-scoped management endpoints.

Path Parameters

tool_uuid*string

Request Body

application/json
  1. body
llm_params?
preset_params?

Response Body

ToolTestResponse with rendered request & live upstream response

Example Request
curl -X POST 'https://api.talkar.in/api/v1/tools/string/test' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
Empty

Mid-Call Tool vs. Post-Call Webhook Node: When to Use Which

NeedUse http_api Tool on Agent NodeUse Post-Call Webhook Node (webhook)
Agent needs the HTTP response to answer the callerYes — response JSON feeds back to the LLM to speak mid-callNo — Webhook Nodes are disconnected (0 edges) and execute asynchronously after the call ends
Must fire deterministically after every finished call (with extracted_data, QA tags, recording & transcript URLs)Risky — LLM chooses when to call mid-call toolsYes — fires reliably after QA & Post-Call Intelligence finish (30s timeout, up to 5 attempts with 30s–600s backoff; dead-letters permanent 4xx)
Custom static secret headers allowed?Yes (overridden by Vault credential if key collides)Static custom_headers with secret keywords (authorization, auth, token, secret, password, passwd, cookie, credential, api-key, apikey, api_key, access-key) are stripped except X-Idempotency-Key — use credential_uuid

For multi-step CRM workflows, keep mid-call http_api responses small and flat—strip huge arrays, nested metadata, and HTML fields in https://n8n.talkar.in before returning JSON to the LLM.


Copy-Paste Sample Payloads

1. Post-Call Webhook payload_template

At webhook delivery time, only call_disposition is auto-injected if your template omits it ("" if unset). Any extracted variables, QA outputs, or media download URLs you want in n8n.talkar.in or your CRM must be mapped explicitly in payload_template:

{
  "run_id": "{{workflow_run_id}}",
  "run_name": "{{workflow_run_name}}",
  "call_time": "{{call_time}}",
  "phone_number": "{{initial_context.called_number | fallback:unknown}}",
  "duration_seconds": "{{cost_info.call_duration_seconds}}",
  "call_disposition": "{{gathered_context.call_disposition | default}}",
  "lead_name": "{{initial_context.customer_name | default}}",
  "intent": "{{extracted_data.intent | default}}",
  "live_vars_json": "{{gathered_context.extracted_variables}}",
  "post_call_json": "{{extracted_data}}",
  "qa_tags_json": "{{annotations.tags}}",
  "recording_url": "{{recording_url}}",
  "transcript_url": "{{transcript_url}}"
}

Webhook render-context & stringification quirks

  • Every placeholder renders to a string in webhooks: Unlike mid-call http_api body_template, webhook payload_template serializes placeholders via string substitution—numbers become strings (e.g. "142" or ""), and dict/list objects such as {{gathered_context.extracted_variables}}, {{extracted_data}}, and {{annotations.tags}} serialize to JSON-encoded strings (json.dumps, so JSON.parse() in n8n.talkar.in, or reference leaf fields directly like {{extracted_data.intent}}).
  • Use | default (not empty | fallback:) for blank defaults: | fallback:}} with nothing after : does not match the template regex and outputs the literal {{...}} token. Use {{var | default}} or plain {{var}} to resolve missing values to "".
  • cost_info holds usage metrics: {{cost_info.call_duration_seconds}} reads usage_info inside webhook templates.
  • extracted_data vs. gathered_context.extracted_variables: Inside webhook templates, {{extracted_data}} is the Post-Call Intelligence (post_call_schema) output, while node-level live extractions sit in {{gathered_context.extracted_variables}} (and top-level {{gathered_context.<key>}}).
  • GET / DELETE webhooks: No JSON body or query string is attached automatically for GET or DELETE webhook nodes; use POST, PUT, or PATCH to send payload_template.

2. Dynamic transfer_call Resolver Request & Response

When a transfer_call tool uses destination_source: "dynamic", Talkar always sends an HTTP POST request (timeout_ms 500–5000, default 3000) with a flat JSON body combining LLM-provided parameters and server-side preset_parameters (presets overwrite same-named LLM keys; no extra run metadata is sent unless added as a preset):

Request sent by Talkar -> your n8n / resolver endpoint (POST)
{
  "department": "billing",
  "account_id": "acct_123"
}

Return HTTP 2xx with transfer_context.destination (required non-empty phone number or SIP target) and optional transfer_context.custom_message (overrides static tool pre-transfer message when non-empty):

Required 200 OK JSON response
{
  "transfer_context": {
    "destination": "+14155550123",
    "custom_message": "Connecting you with our billing specialist now."
  }
}

On this page