Build a connector
A connector lets the assistant do things in another system: find an appointment, check room rates, place an order. It is a manifest plus, when needed, a small JavaScript file. This guide builds a connector for a shop’s order API.
1. Write the manifest
Section titled “1. Write the manifest”{ "id": "com.example.shop", "version": "1.0.0", "name": "Example Shop", "auth": { "type": "api_key", "header": "X-Api-Key" }, "config": { "type": "object", "required": ["base_url"], "properties": { "base_url": { "type": "string", "title": "Shop address" } } }, "permissions": { "hosts": ["{{config.base_url}}"], "scopes": ["records:write"] }, "actions": [ { "name": "getPrice", "description": "Look up the price and stock of a product by name or SKU.", "risk": "read", "confirm": "none", "input": { "type": "object", "required": ["query"], "properties": { "query": { "type": "string" } } }, "output": { "type": "object", "properties": { "price": { "type": "number" }, "in_stock": { "type": "boolean" } } }, "http": { "method": "GET", "url": "{{config.base_url}}/products?q={{input.query}}", "map": { "price": "$.items[0].price", "in_stock": "$.items[0].in_stock" } } }, { "name": "placeOrder", "description": "Place an order after the customer confirmed items, address and total.", "risk": "money", "confirm": "caller", "input": { "type": "object", "required": ["items", "phone"], "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "sku": { "type": "string" }, "qty": { "type": "integer" } } } }, "phone": { "type": "string" }, "address": { "type": "string" } } }, "output": { "type": "object", "properties": { "order_id": { "type": "string" }, "total": { "type": "number" } } }, "js": "placeOrder" } ]}getPrice is a declarative HTTP action. AirFone calls the URL, adds your API key header, and maps the reply with JSONPath. No code runs.
placeOrder needs a few calls in a row, so it is written in JavaScript.
2. Write the JavaScript action
Section titled “2. Write the JavaScript action”export const actions = { async placeOrder(input, ctx) { const base = ctx.config.base_url; const cart = await ctx.http.fetch(`${base}/carts`, { method: 'POST', json: { phone: input.phone } }); for (const item of input.items) { await ctx.http.fetch(`${base}/carts/${cart.json.id}/items`, { method: 'POST', json: item }); } const order = await ctx.http.fetch(`${base}/carts/${cart.json.id}/checkout`, { method: 'POST', json: { address: input.address }, }); ctx.log.info('order placed', { order_id: order.json.id }); await ctx.records.upsert({ phone: input.phone, type: 'order', id: order.json.id, data: order.json }); return { order_id: order.json.id, total: order.json.total }; },};ctx.http.fetch reaches only the hosts in permissions.hosts. The API key is added by AirFone and is never visible to your code.
3. Test it
Section titled “3. Test it”Put the shop’s settings in .airfone/config.json and its API key in .airfone/secrets.json as { "api_key": "..." }. Keep both out of version control.
npx @airfone/connector dev getPrice --input '{"query":"rice"}' # one real call, in the same sandbox as AirFonenpx @airfone/connector test # review checks and every fixture, no networkdev uses the same QuickJS sandbox and limits as AirFone, and reaches only your declared hosts. Add --allow-local to call a mock server on your machine, and --watch to run again on every save. Fixtures go in an action’s fixtures in manifest.json, which AirFone’s review runs too, or in tests/*.json as { "action", "input", "expect" } for your machine only.
4. Sign and upload
Section titled “4. Sign and upload”npx @airfone/connector keygen # once: creates your signing key and prints the public keyAIRFONE_TOKEN=... npx @airfone/connector publish # tests, packs, signs and uploadsRegister the public key in the console under Connectors, Signing keys first. To upload in the console instead, run pack and sign and use Upload version. Automated review runs at once, and the results show next to the version.
See the connectors reference for every manifest field, the full ctx API and limits.