What the agent can do
An action is what separates an assistant that talks from an assistant that does: it adds a customer to your database, checks an order, adds a row to a spreadsheet, sends you a message.
In the conversation, you create an action the same way as everything else: you say what should happen.
When a customer leaves a request, add them to my CRM
Name the service, not a link
Don't go looking for your service's documentation — just name it. The builder will go and look first:
I want the bot to be able to track a shipment with Nova Poshta
It checks whether the service has a machine-readable description (an API specification), a documentation page or a ready-made MCP server, and tells you what it found. If it finds nothing, it says so instead of making up an address. A made-up address looks like a working action and only breaks when a customer uses it.
If you have the documentation at hand, give the link — the builder will pull a list of actions from it right away:
Here's the documentation for our booking system: booking.example.com/api/docs — take what's needed to book a table
Keys and tokens
Access keys don't come with the documentation — you'll have to provide them. The builder will ask directly and explain where to get them.
A key is stored separately, under a name (like CRM_TOKEN), and is never read again — not even by the builder. This isn't overcaution: a bot asked to explain an authorization error would happily quote the key in the conversation.
- A key under a new name is saved right away.
- A key over an existing one — the builder asks, because the old value can't be read and “undo” won't bring anything back here.
- Lost a key? Issue a new one in the service where you got it. A saved key can't be retrieved.
Actions that read, and actions that write
These are different things, and the platform treats them differently.
- Reads (check a status, look up a balance) — the builder adds it right away.
- Writes (place, create, delete) — it asks: the result will land in your system, and the agent will be able to call this action on its own.
If a service has moved
Other people's APIs change their version, domain or path. You don't need to rebuild the action because of that — just say where it goes now:
The CRM moved to /api/v2/leads — change the address in the create_lead action
The access keys stay in place. That's the main point: if you removed the action and created a new one, its headers and the key to the other service would disappear with it — and a saved key can't be retrieved.
The builder asks for consent: the action keeps the same name and description but goes somewhere else — customers will be the first to notice. After the change it offers to test it: the new address may accept something different from the old one.
Remove an action you don't need
Just say it in the conversation:
Remove the get_balance action — it duplicates get_me
The builder asks for consent, and there's a reason: together with the action, its headers and the access keys to the other service disappear. The only way to get it back is to create it again.
This happens a lot alongside imports: someone else's specification brings a dozen actions, including duplicates and extras. Removing them one by one in the conversation is a perfectly normal way to do it — you don't need to go into the panel for that.
It tests every action right away
After creating an action, the builder offers to run it once with sample data — outside any conversation with a customer. Say yes: otherwise a wrong key or address will surface when the agent is answering a real person.
A test is a real call
It really goes to your system: it may create a test order or send an email. That's why the builder asks for consent first.
If the test says “didn't respond in time”, the service most likely isn't broken — it just takes longer than the time the action is given. Say so:
Give this action more time — it's a heavy one
When a service takes minutes (a report, a big data pull, generation), nobody in a customer conversation will wait that long. Work like that belongs in a schedule, and the builder will say so plainly.
Ready-made integrations
The platform has ready-made integrations — you don't need to come up with addresses and parameters, only your access details:
| Integration | What it does | What you'll need |
|---|---|---|
| WooCommerce / Horoshop / Prom.ua — where is my order | tells a shopper where their order is: status, date, delivery, tracking number — only to someone who gave the order number and the phone or email from the order | a key from your store's admin panel; where exactly to get it — Where's my order |
| SkyService — customer card | adds a customer to your SkyService database: name and phone from the conversation | an API token and company ID from your SkyService account |
| Telegram — notify a manager | sends a message to your chat: a new request, an important question | a token for a separate bot from @BotFather and the chat ID |
| Google Sheets — row | adds a row to your spreadsheet | the address of a published Apps Script (https://script.google.com/macros/s/…/exec) |
| Webhook | sends an event to any address of yours: Zapier, Make, n8n, your own service | the webhook address |
Just say what you want to connect:
Connect Google Sheets, here's the script address: https://script.google.com/macros/s/…/exec
The builder sees the list of ready-made integrations and knows what's missing for each — so it asks for exactly what's in the right-hand column. It also asks for consent: the agent will start accessing your system on your behalf, and it will do so on its own.
The same step replaces an expired key
Connecting the same integration a second time updates it rather than adding a second copy of the actions. That's exactly how you replace a key that has expired: ask to connect it again with the new value.
An integration connected long ago can be updated without entering anything again: in the panel, open Actions — below the list are the connected integrations with an “Update actions” button. It pulls in what we've improved over time (descriptions, parameters, how an action reports on long-running work); your addresses and keys stay as they are. If the integration has gained new actions that need your values (a website address, a chat ID), the update doesn't add them and tells you which ones — reconnecting with the same values adds them.
Google Sheets needs a few minutes of setup on Google's side, and a person has to do it:
- Open the spreadsheet → the Extensions menu → Apps Script.
- Write a
doPost(e)function that adds a row to the sheet. - Deploy → New deployment → type Web app, access Anyone.
- Copy the address that looks like
https://script.google.com/macros/s/…/exec.
This address is the access key to your spreadsheet. Don't publish it.
Your own action
If your service isn't on the list, describe in words what should happen, and give the builder the address and key:
The bot needs to be able to create a request in our system. The address is https://crm.example.com/api/leads, method POST, and the key is: …
It creates the action, names it, describes when to call it, and offers to test it right away.
We don't pretend this connects by itself
This is the part where you'll have to put in some work, or call whoever set up that system for you.
Public addresses only
The platform doesn't call addresses on a local or internal network: localhost, 127.0.0.1, 192.168.…, 10.…, 172.16–31.…, 169.254.… and the like — not in actions, not in MCP, not in request nodes, not in page watch, not in links to skills, feeds or calendars. The address that's actually connected to is what gets checked — so a name that leads into such a network won't pass either, nor will a redirect there. A system reachable only from your office or VPN needs a public address (for example, through a secure gateway). The problem report for such an action will say exactly that: “the address leads to an internal network”.
Who exactly is asking: the visitor ID
For an action to call your system on behalf of a specific person — to show their order, plan, booking — the website has to tell the window who the visitor is (data-user-token or YouSelfBot.identify), and the action has a placeholder in that spot:
{{user_token}}
It works in a header value and in the action's address:
Authorization: Bearer {{user_token}}
https://api.firma.com/users/{{user_token}}/ordersThe platform fills in the value itself at the moment of the call; the model doesn't see it and can't write it in. All you need to tell the builder is:
Pass the visitor ID in the Authorization header
Guest. If the website hasn't identified anyone, a header with the placeholder isn't sent at all, and in the address its place is empty — your service sees a request without an ID and decides how to respond.
Values from the page
Whatever the website passed to the window via YouSelfBot.setContext({...}) — a project ID, interface language, plan — the action fills in the same way as the visitor ID: {{context.key}} in the address, a header or a fixed body value.
https://api.firma.com/projects/{{context.project}}/exportIn the export action, put the project the website passed to the window into the address
“Don't call this for guests”
If an action makes no sense for a guest at all, say:
Don't call this action for guests — only for people signed in on the website
Then for a guest this action doesn't exist at all: the assistant doesn't see it, won't promise it and won't call it. This is a platform boundary, not a request to the model. To undo: “let this action work for guests too”.
This isn't proof of identity
The ID is written by the page, so a visitor can substitute someone else's. Checking it is the job of your service (so pass a session token, not a user number) or of a signed envelope.
A link button in the reply
When an action returns an address the person needs to open — a payment page, an order, a booking — the assistant gives it on its own line, and the chat window on your website draws a button:
Your invoice is ready.
[Pay 450 ₴](https://pay.firma.com/i/42)The action doesn't need anything special for this: it's enough for the address to be in its response. The assistant in the window knows this on its own; if you want it to always do so, tell the builder: “give payment links as a button”. The same link in the middle of a sentence stays an ordinary link. Addresses starting with https://, mailto: and tel: become buttons.
What an action shows in the window by itself
An action gets three things to the person without the assistant retelling them — exactly as your service provided them.
Buttons and files. Add top-level buttons and files fields to your service's JSON response:
{
"total": 450,
"buttons": [
{ "label": "Pay 450 ₴", "url": "https://pay.firma.com/i/42", "style": "pay" },
{ "label": "Terms", "url": "https://firma.com/terms", "style": "secondary" }
],
"files": [
{ "name": "Invoice-42.pdf", "url": "https://files.firma.com/i/42.pdf", "size": 20480 }
]
}The window draws buttons (up to four) and “Download” cards (up to five) under the reply; they stay in the conversation even after the page is reloaded. style can be primary (the default), secondary and pay — a payment button, more prominent than the rest. Addresses must be https:// only (for buttons, also mailto: and tel:); anything else simply isn't shown. Nothing needs to be turned on: this works for any action. In other channels (messengers, email) the assistant passes links on as text.
Status label. If an action takes longer than a couple of seconds, give it words for the person:
While it's building the diagram, let it show “Building the diagram…”
While the action runs, the person sees exactly this line instead of three dots.
Raw result — to the page. If the website itself needs to do something with the action's response (apply settings, update the cart), say:
Pass the result of this action to the page
Then the website gets the arguments and the result via the action_result event. By default this doesn't happen: your service's response is seen by the assistant, not by the page's code.
A tool the page runs itself
Not everything the assistant needs to do is a request to your server. Applying settings in an editor, reading code from a field, starting an export in the browser — only the page itself can do these. Tell the builder:
Add a page tool apply_settings: applies settings in the editor; the lang parameter is the interface language
It doesn't need an address or keys. The website's developer registers a handler with the same name — YouSelfBot.registerTool — and the assistant calls it only when the person is on a page where it exists.
How long an action can run
By default, an action waits 30 seconds for a response. If your service needs more, tell the builder (“give this action a minute”) or set timeout_sec. In a conversation the limit is about a minute: a turn itself doesn't last longer. For work that takes minutes or more there's an “accepted now — answered later” protocol: the service immediately returns a job ID, the platform checks the status itself and shows the person a card with progress — describe to the builder how your service says “done”.
Fix an action without deleting it
Rename the action to account_and_plans
Make it clear that this action should only be called after sign-in
Add a section parameter to the action
Name, description, parameters, status label and headers are edited in place: the action's ID, address and keys stay. Via the API — PATCH /v1/dashboard/bots/{id}/skills/{skillID} with the fields name, description, params, headers, status_label, expose_result.
An object or list parameter is described with its own schema (the parameter's schema field) — with nested properties and enum, not as text in the description.
A file from a customer — into your system
A customer dropped a manual, a contract, a CV or a photo of a receipt into the chat — and it needs to end up with you: in your CRM, on your server, in a system that will do something with the file.
The assistant can read the file itself; no action is needed for that. An action is needed to pass the file on. Say:
“Let the customer upload the manual, and the bot will pass it to my system — here's the address: …”
The builder creates an action with one field marked as conversation files. The platform fills it in, not the model: the model can't invent a file “out of thin air” or substitute someone else's — exactly the files the person attached in this conversation go there (the newest, up to ten).
You say in words how the file is sent:
| Say | Your system gets |
|---|---|
| “send the file itself” (the default) | a files list: name, type and the file's content (base64) right in the request. Up to 10 MB per call — if there are more files, the assistant knows not all of them went and will say so |
| “send the file as a link” | a files list: name, type, size and a temporary link url — your system fetches the file itself |
About the link. It's signed and valid for one day (expires_at in the same entry says until when); it opens without a key, as many times as you like during that time — your system can ask for the size first, retry after a dropped connection, put it in a queue. After it expires it no longer works, and it can't be extended: if you need the file longer, keep your own copy. If the file itself is deleted earlier (the conversation retention period ended, the agent was deleted), the link stops working along with it. The link isn't shown to the assistant or the customer.
A connected service (MCP) that needs a file's address gets the same kind of link: the assistant names the file, and the platform fills in the address itself.
After the transfer, the assistant tells the customer what your system replied — so have it reply in words that can be passed on (“received, request No. 5512”).
Ready-made tool sets (MCP)
If your company already has Jira, GitHub or another system with a ready-made MCP server, the agent can get its tools as a whole set — without describing each call separately.
We have an MCP server for Jira, here's the address and token
The builder asks for consent: the agent will act in someone else's system on the owner's behalf. Before connecting, the platform checks that the server really responds and returns a list of tools.
One agent can hand work to another
If a task splits into different roles, you don't have to load everything onto one assistant:
Let the website assistant ask the accounting agent about invoices
This isn't a new step in the flow but a neighbor it turns to. Delegated work spends your credits just like regular work.
Three limits worth knowing in advance:
- it can only call the agents you list. You draw the circle, not the model in the conversation;
- the chain has one link. An agent called by an agent doesn't call anyone else — otherwise two assistants that mention each other would go round in circles until the wallet runs dry;
- the agent doing the work has the caller's permissions. If a customer is asking the website bot, the colleague does exactly what's allowed in a conversation with a customer — not everything it can do on your behalf.
When an action affects people
Three things you ask for in words, which the platform enforces as a boundary, not as a request. The difference matters: a rule written into the agent's character can be bypassed by it; these three can't.
“Let me confirm first”
Emails to customers should go out only with my approval
The action stops running on its own: the agent prepares it and stops. The panel shows “1 action is waiting for your approval”, and until you click, nothing has happened in the world. You see who it goes to and what exactly will be sent, and you can edit the text before sending or reject it.
The card lives for one day. A “we'll call you back today” approved a week later is worse than nothing, so expired ones are shown separately — that's not your rejection, it means the queue grows faster than it's being handled.
It's worth turning this on for everything that goes out in the company's name: emails, posts, payments. Changed your mind? Say “stop asking me every time”, and the action will run on its own again.
“No more than twenty from each account”
Post no more than 15 a day from each page
It's the account that gets banned, not the action. If one action uses several accounts — three pages, two sender numbers — name the parameter that holds “who it's from” (quota_param), and the daily limit is counted for each separately. A shared limit across three either stops the work three times too early or exceeds the safe rate on each three times over.
“Don't write to people who opted out”
If a person asked not to be contacted — never again. And no more than once every three days to one person, and no more than twenty a day
The agent itself keeps the stop list: when a person in a conversation asks not to be bothered, it records that right away. You can add a contact too — and cross it out if it was added by mistake.
The stop list is about the person, not the action: it applies to every way the agent could reach them. Any spelling works — “+38 (067) 111-00-00” and “0671110000” are the same person.
And if the person changes their mind. They'll tell the agent — no one else. The agent can't restore them: if it could, the stop list could be bypassed with one sentence in the chat. So it records the request and says honestly: “I'll pass this on to the owner; it's their decision.” You'll see it right away — as a notification, as the “Asking to resume” row in the panel, and as the request itself in the list, in their own words. The button on such a row is called “Restore”.
Before promising anything to anyone, the agent checks the stop list: a promise silently blocked by a limit is worse than an honest “you're marked as not wanting emails”.
Why you shouldn't write this into the agent's character
A rule in the character is a request. The agent doesn't see how many emails went out yesterday and doesn't remember who has already opted out: at the hundredth email of a mailing, it simply doesn't have that in front of it. A limit, on the other hand, is counted by the platform and stops the call from happening.
“Post from the client's account, not mine”
Let it post on the client's Instagram, not ours
A key in an action is you: your account, your data. For the agent to do something in someone else's account, the account owner has to give consent in the service itself — and then the agent gets access to exactly that account.
This takes two steps, and the second one isn't done by you:
- the builder sets up the connection and gives you a link;
- the person whose account is being connected opens that link and clicks “allow”.
Until the second step is done, the panel shows “connections without consent” — the connection looks set up but doesn't work, and we say so plainly instead of waiting for the action to break on a customer.
After “allow”, the person lands on a page that says access is connected — they're not our user, and a sign-in form is no use to them. If the link has expired (it lives for five minutes), the same page says what to do: ask for a new one.
Exactly the permissions that were requested. The agent doesn't do “anything at all” in someone else's account, only the actions you described, within the permissions granted. When the person revokes access on their side, you find out from a line in notifications, not from an action that silently stopped working.
Access renews itself. If the service didn't provide a refresh key, the agent tells you right away: such access will end by itself in an hour or two.
How many actions it has now
The Actions row in the panel. Or ask: “what actions can it do?”.
Next
- Where the agent answers — so customers finally get to use the action.
- When something breaks — what to do when an action stops working.