---
title: Skills
sidebar_position: 5
---

# Skills

Skills let the AI take actions in your systems during a conversation. When a user asks something that requires real data — order status, account balance, appointment availability — the AI calls the relevant skill, gets the data, and continues responding with accurate information.

## How skills work

Skills are built on AI function calling:

1. You define a skill in the Elaras dashboard with a name, description, parameter schema, and your endpoint URL.
2. When the AI decides to use a skill, Elaras sends a `POST` request to your endpoint with the extracted parameters.
3. Your endpoint processes the request and returns a JSON response.
4. Elaras injects the response into the conversation context and the AI continues generating its reply.

The AI decides **when** to call a skill — you do not call it manually. The description you write tells the AI what the skill does and when to use it.

## Registering a skill

In the Elaras dashboard, go to your chatbot's **Skills** tab and click **Add skill**.

| Field | Description |
|---|---|
| **Name** | Lowercase snake_case identifier, e.g. `get_order_status`. This is what the AI sees. |
| **Description** | Plain English explanation of what the skill does and when to use it. The AI reads this to decide when to call it. |
| **Parameters** | JSON Schema object describing the parameters. The AI extracts these from the conversation. |
| **Endpoint URL** | The URL Elaras will POST to when the skill is called. Must be publicly reachable. |
| **API key** | A secret you generate. Elaras sends this as `Authorization: Bearer <api_key>` and uses it to sign the request body. |

## The request Elaras sends

When the AI calls a skill, Elaras POSTs to your endpoint:

```http
POST https://your-endpoint.com/skills/get-order-status
Authorization: Bearer your_api_key_here
X-Elaras-Signature: sha256=a1b2c3d4e5f6...
Content-Type: application/json

{
  "skill": "get_order_status",
  "params": {
    "order_id": "12345"
  },
  "session": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "lead_name": "Jordan",
    "lead_email": "jordan@example.com"
  }
}
```

| Field | Description |
|---|---|
| `skill` | The skill name as registered in the dashboard |
| `params` | The parameters the AI extracted from the conversation, matching your JSON Schema |
| `session.id` | The current session UUID |
| `session.lead_name` | Lead name if captured, otherwise `null` |
| `session.lead_email` | Lead email if captured, otherwise `null` |

## Verifying the signature

The `X-Elaras-Signature` header contains `sha256=` followed by the HMAC-SHA256 of the raw request body, using your API key as the secret. Always verify this before processing a request.

### Node.js

```js
import crypto from 'crypto';

function verifySignature(rawBody, signature, apiKey) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', apiKey)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}

// Express example
app.post('/skills/get-order-status', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-elaras-signature'];

  if (!verifySignature(req.body, signature, process.env.ELARAS_SKILL_API_KEY)) {
    return res.status(401).json({error: 'Invalid signature'});
  }

  const {skill, params, session} = JSON.parse(req.body);

  // Handle the skill request...
  const order = getOrder(params.order_id);

  res.json({
    order_id: order.id,
    status: order.status,
    estimated_delivery: order.estimated_delivery,
  });
});
```

### PHP

```php
<?php

function verifyElarasSignature(string $rawBody, string $signature, string $apiKey): bool
{
    $expected = 'sha256=' . hash_hmac('sha256', $rawBody, $apiKey);
    return hash_equals($expected, $signature);
}

$rawBody  = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_ELARAS_SIGNATURE'] ?? '';
$apiKey   = getenv('ELARAS_SKILL_API_KEY');

if (!verifyElarasSignature($rawBody, $signature, $apiKey)) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

$payload = json_decode($rawBody, true);
$params  = $payload['params'];

// Handle the skill request...
$order = getOrder($params['order_id']);

header('Content-Type: application/json');
echo json_encode([
    'order_id'           => $order['id'],
    'status'             => $order['status'],
    'estimated_delivery' => $order['estimated_delivery'],
]);
```

## Expected response

Return any JSON object or string. The response is passed directly to the AI as the skill result:

```json
{
  "order_id": "12345",
  "status": "shipped",
  "estimated_delivery": "2026-07-26",
  "tracking_url": "https://track.example.com/12345"
}
```

The AI will then incorporate this data into its next response to the user.

## Parameter JSON Schema example

Here is a complete skill definition for an order status lookup:

**Name:** `get_order_status`

**Description:** Look up the status of a customer order. Call this when the user asks about their order, delivery, or tracking information.

**Parameters schema:**

```json
{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "description": "The order ID or order number mentioned by the user."
    }
  },
  "required": ["order_id"]
}
```

The AI extracts `order_id` from the conversation (e.g. "my order 12345") and passes it in `params.order_id`.

## Error handling

If your skill encounters an error, return a descriptive error message as a string or JSON — do **not** return a 4xx or 5xx HTTP status. A non-2xx response tells Elaras the skill invocation itself failed (network/infra error), which is different from a business logic error.

Good:

```json
{ "error": "Order 99999 not found. Please check the order number and try again." }
```

Bad (causes a skill invocation failure, retried by Elaras):

```
HTTP 404 Not Found
```

## Testing skills

Use the **Test** button on the skill configuration page in the dashboard. You can send a test payload with custom `params` and inspect the response your endpoint returns. The test call is signed identically to real calls so your signature verification will run.

## Best practices

- **Write a precise description.** The AI uses it to decide when to call the skill. Be specific about what data the skill provides and what questions it answers.
- **Keep responses concise.** Return only the data the AI needs to answer the question. Large payloads consume more context window and credits.
- **Validate params server-side.** The AI extracts params from natural language — always validate and sanitise them before using them in database queries.
- **Return actionable error messages.** If something goes wrong, return a message the AI can relay to the user ("I could not find that order — could you double-check the number?").
- **Respond within 10 seconds.** Elaras times out skill calls after 10 seconds. For long-running operations, fetch the data asynchronously before the conversation starts or cache recent results.
