Crear una funcion de webhook
curl --request POST \
--url https://api.keebai.com/v1/functions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "consultar_stock",
"display_name": "Consultar stock",
"description": "Devuelve el stock disponible de un SKU en el ERP.",
"context": {
"url": "https://erp.example.com/stock",
"method": "POST",
"headers": {
"Authorization": "Bearer sk_live_xxx"
},
"params": {},
"body": {
"sku": "{{sku}}"
},
"timeout": 30,
"response_path": "<string>"
},
"tool_params": [
{
"name": "<string>",
"description": "<string>",
"type": "string",
"required": true
}
],
"tags": [
"<string>"
],
"is_active": true
}
'import requests
url = "https://api.keebai.com/v1/functions"
payload = {
"name": "consultar_stock",
"display_name": "Consultar stock",
"description": "Devuelve el stock disponible de un SKU en el ERP.",
"context": {
"url": "https://erp.example.com/stock",
"method": "POST",
"headers": { "Authorization": "Bearer sk_live_xxx" },
"params": {},
"body": { "sku": "{{sku}}" },
"timeout": 30,
"response_path": "<string>"
},
"tool_params": [
{
"name": "<string>",
"description": "<string>",
"type": "string",
"required": True
}
],
"tags": ["<string>"],
"is_active": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'consultar_stock',
display_name: 'Consultar stock',
description: 'Devuelve el stock disponible de un SKU en el ERP.',
context: {
url: 'https://erp.example.com/stock',
method: 'POST',
headers: {Authorization: 'Bearer sk_live_xxx'},
params: {},
body: JSON.stringify({sku: '{{sku}}'}),
timeout: 30,
response_path: '<string>'
},
tool_params: [{name: '<string>', description: '<string>', type: 'string', required: true}],
tags: ['<string>'],
is_active: true
})
};
fetch('https://api.keebai.com/v1/functions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.keebai.com/v1/functions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'consultar_stock',
'display_name' => 'Consultar stock',
'description' => 'Devuelve el stock disponible de un SKU en el ERP.',
'context' => [
'url' => 'https://erp.example.com/stock',
'method' => 'POST',
'headers' => [
'Authorization' => 'Bearer sk_live_xxx'
],
'params' => [
],
'body' => [
'sku' => '{{sku}}'
],
'timeout' => 30,
'response_path' => '<string>'
],
'tool_params' => [
[
'name' => '<string>',
'description' => '<string>',
'type' => 'string',
'required' => true
]
],
'tags' => [
'<string>'
],
'is_active' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.keebai.com/v1/functions"
payload := strings.NewReader("{\n \"name\": \"consultar_stock\",\n \"display_name\": \"Consultar stock\",\n \"description\": \"Devuelve el stock disponible de un SKU en el ERP.\",\n \"context\": {\n \"url\": \"https://erp.example.com/stock\",\n \"method\": \"POST\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_xxx\"\n },\n \"params\": {},\n \"body\": {\n \"sku\": \"{{sku}}\"\n },\n \"timeout\": 30,\n \"response_path\": \"<string>\"\n },\n \"tool_params\": [\n {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"type\": \"string\",\n \"required\": true\n }\n ],\n \"tags\": [\n \"<string>\"\n ],\n \"is_active\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.keebai.com/v1/functions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"consultar_stock\",\n \"display_name\": \"Consultar stock\",\n \"description\": \"Devuelve el stock disponible de un SKU en el ERP.\",\n \"context\": {\n \"url\": \"https://erp.example.com/stock\",\n \"method\": \"POST\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_xxx\"\n },\n \"params\": {},\n \"body\": {\n \"sku\": \"{{sku}}\"\n },\n \"timeout\": 30,\n \"response_path\": \"<string>\"\n },\n \"tool_params\": [\n {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"type\": \"string\",\n \"required\": true\n }\n ],\n \"tags\": [\n \"<string>\"\n ],\n \"is_active\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.keebai.com/v1/functions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"consultar_stock\",\n \"display_name\": \"Consultar stock\",\n \"description\": \"Devuelve el stock disponible de un SKU en el ERP.\",\n \"context\": {\n \"url\": \"https://erp.example.com/stock\",\n \"method\": \"POST\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_xxx\"\n },\n \"params\": {},\n \"body\": {\n \"sku\": \"{{sku}}\"\n },\n \"timeout\": 30,\n \"response_path\": \"<string>\"\n },\n \"tool_params\": [\n {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"type\": \"string\",\n \"required\": true\n }\n ],\n \"tags\": [\n \"<string>\"\n ],\n \"is_active\": true\n}"
response = http.request(request)
puts response.read_body{
"function": {
"_id": "<string>",
"name": "<string>",
"display_name": "<string>",
"description": "<string>",
"type": "webhook",
"tool_params": [
{
"name": "<string>",
"required": true,
"description": "<string>"
}
],
"context": {
"url": "<string>",
"headers": {
"Authorization": "***"
},
"params": {},
"timeout": 123,
"body": {},
"response_path": "<string>"
},
"tags": [
"<string>"
],
"is_active": true,
"used_by_blocks": [
{
"block_id": "<string>",
"name": "<string>"
}
],
"created_at": "<string>",
"updated_at": "<string>"
}
}Functions
POST /v1/functions
Create a webhook function an assistant can call, with its URL, headers and parameters.
POST
/
v1
/
functions
Crear una funcion de webhook
curl --request POST \
--url https://api.keebai.com/v1/functions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "consultar_stock",
"display_name": "Consultar stock",
"description": "Devuelve el stock disponible de un SKU en el ERP.",
"context": {
"url": "https://erp.example.com/stock",
"method": "POST",
"headers": {
"Authorization": "Bearer sk_live_xxx"
},
"params": {},
"body": {
"sku": "{{sku}}"
},
"timeout": 30,
"response_path": "<string>"
},
"tool_params": [
{
"name": "<string>",
"description": "<string>",
"type": "string",
"required": true
}
],
"tags": [
"<string>"
],
"is_active": true
}
'import requests
url = "https://api.keebai.com/v1/functions"
payload = {
"name": "consultar_stock",
"display_name": "Consultar stock",
"description": "Devuelve el stock disponible de un SKU en el ERP.",
"context": {
"url": "https://erp.example.com/stock",
"method": "POST",
"headers": { "Authorization": "Bearer sk_live_xxx" },
"params": {},
"body": { "sku": "{{sku}}" },
"timeout": 30,
"response_path": "<string>"
},
"tool_params": [
{
"name": "<string>",
"description": "<string>",
"type": "string",
"required": True
}
],
"tags": ["<string>"],
"is_active": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'consultar_stock',
display_name: 'Consultar stock',
description: 'Devuelve el stock disponible de un SKU en el ERP.',
context: {
url: 'https://erp.example.com/stock',
method: 'POST',
headers: {Authorization: 'Bearer sk_live_xxx'},
params: {},
body: JSON.stringify({sku: '{{sku}}'}),
timeout: 30,
response_path: '<string>'
},
tool_params: [{name: '<string>', description: '<string>', type: 'string', required: true}],
tags: ['<string>'],
is_active: true
})
};
fetch('https://api.keebai.com/v1/functions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.keebai.com/v1/functions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'consultar_stock',
'display_name' => 'Consultar stock',
'description' => 'Devuelve el stock disponible de un SKU en el ERP.',
'context' => [
'url' => 'https://erp.example.com/stock',
'method' => 'POST',
'headers' => [
'Authorization' => 'Bearer sk_live_xxx'
],
'params' => [
],
'body' => [
'sku' => '{{sku}}'
],
'timeout' => 30,
'response_path' => '<string>'
],
'tool_params' => [
[
'name' => '<string>',
'description' => '<string>',
'type' => 'string',
'required' => true
]
],
'tags' => [
'<string>'
],
'is_active' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.keebai.com/v1/functions"
payload := strings.NewReader("{\n \"name\": \"consultar_stock\",\n \"display_name\": \"Consultar stock\",\n \"description\": \"Devuelve el stock disponible de un SKU en el ERP.\",\n \"context\": {\n \"url\": \"https://erp.example.com/stock\",\n \"method\": \"POST\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_xxx\"\n },\n \"params\": {},\n \"body\": {\n \"sku\": \"{{sku}}\"\n },\n \"timeout\": 30,\n \"response_path\": \"<string>\"\n },\n \"tool_params\": [\n {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"type\": \"string\",\n \"required\": true\n }\n ],\n \"tags\": [\n \"<string>\"\n ],\n \"is_active\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.keebai.com/v1/functions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"consultar_stock\",\n \"display_name\": \"Consultar stock\",\n \"description\": \"Devuelve el stock disponible de un SKU en el ERP.\",\n \"context\": {\n \"url\": \"https://erp.example.com/stock\",\n \"method\": \"POST\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_xxx\"\n },\n \"params\": {},\n \"body\": {\n \"sku\": \"{{sku}}\"\n },\n \"timeout\": 30,\n \"response_path\": \"<string>\"\n },\n \"tool_params\": [\n {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"type\": \"string\",\n \"required\": true\n }\n ],\n \"tags\": [\n \"<string>\"\n ],\n \"is_active\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.keebai.com/v1/functions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"consultar_stock\",\n \"display_name\": \"Consultar stock\",\n \"description\": \"Devuelve el stock disponible de un SKU en el ERP.\",\n \"context\": {\n \"url\": \"https://erp.example.com/stock\",\n \"method\": \"POST\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_xxx\"\n },\n \"params\": {},\n \"body\": {\n \"sku\": \"{{sku}}\"\n },\n \"timeout\": 30,\n \"response_path\": \"<string>\"\n },\n \"tool_params\": [\n {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"type\": \"string\",\n \"required\": true\n }\n ],\n \"tags\": [\n \"<string>\"\n ],\n \"is_active\": true\n}"
response = http.request(request)
puts response.read_body{
"function": {
"_id": "<string>",
"name": "<string>",
"display_name": "<string>",
"description": "<string>",
"type": "webhook",
"tool_params": [
{
"name": "<string>",
"required": true,
"description": "<string>"
}
],
"context": {
"url": "<string>",
"headers": {
"Authorization": "***"
},
"params": {},
"timeout": 123,
"body": {},
"response_path": "<string>"
},
"tags": [
"<string>"
],
"is_active": true,
"used_by_blocks": [
{
"block_id": "<string>",
"name": "<string>"
}
],
"created_at": "<string>",
"updated_at": "<string>"
}
}Creates a webhook function. You describe what it does and which arguments it takes; the model decides when to call it and with what.
Placeholders written
{{name}} in the path, the query string or the body are filled at call time from the arguments the model chose, so https://erp.example.com/stock/{{sku}} with a sku parameter is the normal shape. Not in the host — read why.
Endpoint
POST https://api.keebai.com/v1/functions
Required scope
assistants:write
Headers
| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer kbai_pk_<token> |
Content-Type | Yes | application/json |
Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Machine name, unique per company. Up to 100 characters. The model calls it by this. |
display_name | string | Yes | Up to 200 characters. Shown in the portal. |
description | string | Yes | Up to 1000 characters. The model reads this to decide when to call the function — write it for the model, not for a human. |
context | object | Yes | The HTTP request to make. |
context.url | string | Yes | Public https URL. Validated — see the rules. |
context.method | string | No | GET, POST, PUT, PATCH or DELETE. Defaults to POST. |
context.headers | object | No | String values. Write-only: reads return ***. |
context.params | object | No | Query string. Write-only, same as headers. |
context.body | object | No | Request body template. Returned unmasked. |
context.timeout | integer | No | 1–30 seconds. Defaults to 30. |
context.response_path | string | No | Dotted path into the JSON response to extract, e.g. data.items. Without it the whole response is handed to the model. |
tool_params | object[] | No | Up to 30 arguments the model can supply. |
tool_params[].name | string | Yes | Argument name, matching the {{placeholder}}. |
tool_params[].description | string | No | What it means. The model reads this too. |
tool_params[].type | string | No | string, number, boolean, array or object. Defaults to string. |
tool_params[].required | boolean | No | Defaults to true. |
tags | string[] | No | Up to 20, for grouping in the portal. |
is_active | boolean | No | Defaults to true. |
Example request
curl -X POST https://api.keebai.com/v1/functions \
-H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "consultar_stock",
"display_name": "Consultar stock",
"description": "Devuelve el stock disponible de un SKU en el ERP. Usar antes de prometer disponibilidad.",
"context": {
"url": "https://erp.example.com/stock/{{sku}}",
"method": "GET",
"headers": { "Authorization": "Bearer erp_live_xxx" },
"timeout": 10,
"response_path": "data.available"
},
"tool_params": [
{ "name": "sku", "description": "Código del producto", "required": true }
],
"tags": ["erp"]
}'
const resp = await fetch("https://api.keebai.com/v1/functions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "consultar_stock",
display_name: "Consultar stock",
description: "Devuelve el stock disponible de un SKU en el ERP.",
context: {
url: "https://erp.example.com/stock/{{sku}}",
method: "GET",
headers: { Authorization: `Bearer ${process.env.ERP_TOKEN}` },
timeout: 10,
},
tool_params: [{ name: "sku", description: "Código del producto" }],
}),
});
if (resp.status === 400) {
const { error } = await resp.json();
if (error.code === "FUNCTION_URL_NOT_ALLOWED") {
console.error(error.details.reason);
}
}
import os, requests
resp = requests.post(
"https://api.keebai.com/v1/functions",
headers={"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"},
json={
"name": "consultar_stock",
"display_name": "Consultar stock",
"description": "Devuelve el stock disponible de un SKU en el ERP.",
"context": {
"url": "https://erp.example.com/stock/{{sku}}",
"method": "GET",
"headers": {"Authorization": f"Bearer {os.environ['ERP_TOKEN']}"},
"timeout": 10,
},
"tool_params": [{"name": "sku", "description": "Código del producto"}],
},
timeout=10,
)
resp.raise_for_status()
fn = resp.json()["function"]
Response
201 Created
The function, same shape asGET /v1/functions/{id}, with the header values already masked.
400 Bad Request
A missing required field, a field over its limit, or an unknown property. Two specific codes:FUNCTION_URL_NOT_ALLOWED— the URL failed validation.details.reasonnames the rule; see the table.FUNCTION_TIMEOUT_NOT_ALLOWED—timeoutoutside 1–30 seconds.
401 Unauthorized
Missing, invalid, revoked, or expired token.403 Forbidden
The token does not have theassistants:write scope.
409 Conflict
A function with thatname already exists in the company.
502 Bad Gateway
The assistant service is unreachable.Operational notes
descriptionis a prompt, not documentation. It is the only thing the model reads when deciding whether to call the function, so say when to use it and when not to. “Devuelve el stock. Usar antes de prometer disponibilidad” beats “Endpoint de stock”.- A
tool_paramsentry with no matching placeholder is still passed to the model, which will supply it and see it go nowhere. Keep the two in sync. nameis unique per company, not per project. A409can come from a function created under a different project.- Creating a function does not attach it to anything. The model can only call functions bound to a block in the assistant’s prompt; that binding is done in the portal.
typeis forced towebhook. Sending anything else is rejected, and there is no way to create a calendar, ecommerce or block-managed function through this API.
Authorizations
Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.
Body
application/json
Nombre tecnico, unico por company. Es el nombre con el que el modelo invoca la funcion.
Maximum string length:
100Example:
"consultar_stock"
Maximum string length:
200Example:
"Consultar stock"
Para que sirve la funcion. Es lo que lee el modelo para decidir cuando llamarla.
Maximum string length:
1000Example:
"Devuelve el stock disponible de un SKU en el ERP."
Show child attributes
Show child attributes
Maximum array length:
30Show child attributes
Show child attributes
Maximum array length:
20Response
201 - application/json
Show child attributes
Show child attributes
Was this page helpful?
⌘I