> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nixflex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom functions

> Let the agent call your APIs mid-call

Custom functions extend the agent beyond built-in actions like SMS and transfer. You define a function in the dashboard (name, description, your URL, what parameters the agent can pass), and the agent can call it during the conversation — to look up customer records, check inventory, book appointments, anything your API can do.

## How it works

<Steps>
  <Step title="Create the function in the dashboard">
    Open your agent, expand the **Custom Functions** panel, and add a function. You give it a name, a description (the agent reads this every turn), an HTTPS URL, and a parameter schema.
  </Step>

  <Step title="The agent sees it as a tool">
    On every conversation turn, the agent is aware of every active function on the agent. Based on the description you wrote, the agent decides when to call it.
  </Step>

  <Step title="Engine calls your URL">
    When the agent triggers a function, the engine makes an HTTP request to your URL with a JSON body containing the call context, what the agent extracted, and 14 dynamic variables.
  </Step>

  <Step title="Your endpoint replies with JSON">
    You return a JSON response. The engine reads the `result` field (if present) and feeds it back to the agent as the tool result.
  </Step>

  <Step title="The agent responds to the caller">
    The agent uses the data you returned to compose the next reply, spoken naturally to the caller via TTS.
  </Step>
</Steps>

## What your endpoint receives

Every request from the engine looks like this:

```json theme={null}
{
  "name": "check_appointment",
  "args": {
    "date": "2026-03-08",
    "time": "14:00"
  },
  "variables": {
    "caller_phone": "+447386172392",
    "business_phone": "+447446466847",
    "current_date": "Wednesday, May 27, 2026",
    "current_iso_date": "2026-05-27",
    "current_time": "14:32",
    "current_iso_time": "14:32:18",
    "tomorrow_date": "Thursday, May 28, 2026",
    "tomorrow_iso_date": "2026-05-28",
    "current_day_of_week": "Wednesday",
    "current_timezone": "Europe/London",
    "call_direction": "inbound",
    "call_id": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "agent_id": "agent_f2b06c0e0bd877e9",
    "caller_history": "PREVIOUS CALLS FROM THIS NUMBER..."
  }
}
```

Three top-level fields:

| Field       | What it is                                                                                           |
| ----------- | ---------------------------------------------------------------------------------------------------- |
| `name`      | The name of the function being called.                                                               |
| `args`      | What the agent extracted from the conversation, matching your parameter schema.                      |
| `variables` | All 14 dynamic variables. Sent on **every** request automatically — you do not need to ask for them. |

The only header you receive is `User-Agent: Nixflex-VoiceEngine/1.0`. There is no signature header yet (see [Authentication](#authentication) below).

## The 14 dynamic variables

These are always present in `variables` on every request. They cover everything the engine knows about the call.

| Variable              | Description                                                             | Example                              |
| --------------------- | ----------------------------------------------------------------------- | ------------------------------------ |
| `caller_phone`        | Caller phone in E.164 format                                            | `+447386172392`                      |
| `business_phone`      | The number the caller dialed                                            | `+447446466847`                      |
| `current_date`        | Today, human-readable (agent timezone)                                  | `Wednesday, May 27, 2026`            |
| `current_iso_date`    | Today, ISO format                                                       | `2026-05-27`                         |
| `current_time`        | Now, 24h `HH:MM`                                                        | `14:32`                              |
| `current_iso_time`    | Now, ISO time format                                                    | `14:32:18`                           |
| `tomorrow_date`       | Tomorrow, human-readable                                                | `Thursday, May 28, 2026`             |
| `tomorrow_iso_date`   | Tomorrow, ISO format                                                    | `2026-05-28`                         |
| `current_day_of_week` | Day name                                                                | `Wednesday`                          |
| `current_timezone`    | Agent timezone setting                                                  | `Europe/London`                      |
| `call_direction`      | `inbound` or `outbound`                                                 | `inbound`                            |
| `call_id`             | The Twilio call SID                                                     | `CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` |
| `agent_id`            | The agent's ID                                                          | `agent_f2b06c0e0bd877e9`             |
| `caller_history`      | Summaries of past calls from this number (empty for first-time callers) | `PREVIOUS CALLS FROM THIS NUMBER...` |

<Note>
  There is no `caller_name` variable. Speech-to-text often mishears names. If a name matters to your flow, instruct the agent in its system prompt to ask the caller to spell it and confirm it back before calling the function.
</Note>

## What to send back

Your endpoint should respond with JSON. Two valid shapes:

**Recommended — wrap the data in a `result` field:**

```json theme={null}
{
  "result": {
    "available": true,
    "alternatives": ["14:30", "15:00", "15:30"]
  }
}
```

The engine extracts `result` and feeds just that to the agent. Cleaner context, faster agent responses.

**Also accepted — return the data at the top level:**

```json theme={null}
{
  "available": true,
  "alternatives": ["14:30", "15:00", "15:30"]
}
```

If there's no `result` field, the engine stringifies the whole body and feeds it to the agent.

### Status codes

| Code                       | Behaviour                                                                                                   |
| -------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `200`                      | Success. Body is parsed and passed to the agent.                                                            |
| `4xx` / `5xx`              | Treated as a function error. The agent is told the function failed and can apologise or transfer naturally. |
| No response within timeout | Treated as a function error. Default timeout is 5 seconds.                                                  |

Errors never crash the call — the agent always handles them gracefully.

## Writing a good description

This is the most important field on the whole function. The agent reads your description on **every** conversation turn to decide whether to call the function. A vague description means the agent either never fires it, or fires it at the wrong time.

**Bad:**

```
Helps the AI know when to call this function
```

The agent has no idea when this applies. The function effectively doesn't exist.

**Good:**

```
Check the status of a customer's order. Use when the caller asks about delivery, order status, where their order is, or anything about a previously placed order.
```

The agent now knows exactly when to fire. Will trigger reliably whenever the conversation matches.

<Tip>
  Write the description as instructions to the agent, not as a label for humans. Always include the words "Use when..." followed by the conversational cues that should trigger it.
</Tip>

## URL and header variable substitution

Even though all 14 variables are sent in the request body automatically, sometimes you need them in the URL itself (REST APIs) or in a header (authentication). Use `{{variable_name}}` syntax in the URL or Headers fields.

### URL substitution

```
https://api.example.com/customers/{{caller_phone}}/orders
```

Becomes:

```
https://api.example.com/customers/+447386172392/orders
```

### Header substitution

Headers also support substitution, useful for passing call context through to upstream services that expect it in headers.

```
X-Call-Id: {{call_id}}
X-Caller-Phone: {{caller_phone}}
```

### When to use which

| Your API style                        | Where data goes  | Use                            |
| ------------------------------------- | ---------------- | ------------------------------ |
| REST resource (`GET /customers/{id}`) | URL path         | `{{variable}}` in URL          |
| REST query (`GET /search?phone=x`)    | URL query string | `{{variable}}` in URL          |
| JSON POST endpoint                    | Request body     | Read from `req.body.variables` |

Most modern APIs use the third pattern, so most developers leave the URL static and just read variables from the body. URL substitution is for REST-style and GET endpoints.

<Note>
  Substitution **only** works in URL and Headers. It does not substitute in the description, name, or parameter schema fields.
</Note>

## Authentication

Custom function endpoints are public by default — the engine identifies itself with `User-Agent: Nixflex-VoiceEngine/1.0` but does not yet sign requests.

For now, the recommended pattern is a **bearer token in the Headers field**:

```
Authorization: Bearer your-shared-secret-here
```

Add it once in the Headers field on the function, and the engine sends it on every request. Verify it on your side before processing.

<Tip>
  Use a long random shared secret (32+ characters). Treat it like a password — anyone with it can call your function URL pretending to be Nixflex.
</Tip>

<Note>
  Request signing (HMAC with rotating secrets) is on the roadmap. When it lands, the engine will send an `X-Nixflex-Signature` header you can verify with a per-agent secret. Until then, use bearer tokens in Headers.
</Note>

## Mentioning functions in the prompt

You can let the agent discover when to use functions purely from the descriptions, but adding an **Actions** section to the system prompt makes triggering more reliable:

```
ACTIONS
- Before promising any appointment time, check availability first using the check_availability function.
- Once the caller confirms a time, create the booking using the create_booking function.
- For order status questions, look up the order using the get_order function.
```

This gives the agent an extra hint about ordering and conditions on top of each function's description.

## Common use cases

<CardGroup cols={2}>
  <Card title="Calendar lookups" icon="calendar">
    Check availability before promising a slot.
  </Card>

  <Card title="CRM record fetches" icon="address-book">
    Pull customer history when they call.
  </Card>

  <Card title="Booking creation" icon="calendar-plus">
    Create the booking in your system once confirmed.
  </Card>

  <Card title="Inventory checks" icon="box">
    Verify a product is in stock before quoting.
  </Card>

  <Card title="Order status" icon="truck">
    "Where's my order?" — look it up and reply naturally.
  </Card>

  <Card title="Payment links" icon="credit-card">
    Generate a Stripe checkout URL and text it via `[SEND_SMS:]`.
  </Card>
</CardGroup>

## Limits and constraints

| Limit                | Value                     | Why                                                                                                      |
| -------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------- |
| Functions per agent  | 10                        | Keeps the agent's tool list focused. Too many functions and the agent gets confused about which to call. |
| URL scheme           | HTTPS only                | Plain HTTP is rejected for security.                                                                     |
| Default timeout      | 5 seconds                 | After this, the function is treated as failed. Override per-function if your API is slow.                |
| Response size        | 100 KB max                | Larger responses are rejected. Big payloads also bloat the agent's context.                              |
| Tool rounds per turn | 3                         | Stops the agent from looping on the same function indefinitely.                                          |
| Function name format | snake\_case, max 64 chars | Matches the agent's tool name requirements.                                                              |

## Limitations

* Functions are triggered by the agent during conversation — you cannot trigger them externally.
* The engine does not retry failed functions automatically. Make your endpoint idempotent if the agent might call it twice.
* Function calls happen **per conversation turn**. There is no streaming partial results back to the agent.
* The tool-aware path is slightly slower than the no-functions path because it waits for the full agent response (including tool calls) before speaking.

## Testing your function

The dashboard has a **Test Webhook** button on every function. It sends a request to your URL with:

* Fake `caller_phone` (`+44XXXXXXXXXX`)
* Fake `business_phone` (`+44XXXXXXXXXX`)
* Fake `call_id` (`test_<timestamp>`)
* Real date/time variables (from your agent's timezone setting)
* Args you type into the **Sample Args** field

Use it to confirm your endpoint is reachable, returns the right shape, and responds within the timeout. The actual production call will use real caller numbers and real `call_id`.

<Tip>
  The test panel is reachability-focused. To test a complete real-world flow with a real caller number, just make a real call to your agent.
</Tip>

For more standardised integrations (Stripe, Salesforce, Google Calendar etc.), MCP server support is on the roadmap.
