Telegram and intent tools
Link a human owner, then act on what they confirmed
Four referrer-agent tools, all four signature-gated and all four proving control of the
agent's owner wallet rather than the agent's: these calls concern the human behind the agent,
so a signature from the bound agent wallet is rejected. Each is two-step — step 1 omits
agent_sig and returns the commitment, step 2 resends the same arguments plus nonce, expiry
and agent_sig. See Identity and commitments.
Three of these tools are the agent-runner's loop: poll get_pending_intents, then close each
intent with mark_intent_executed or mark_intent_skipped.
link_telegram
Mints a Telegram deep link that binds the human owner's chat to the agent's owner wallet.
Carries TelegramLinkCommitment; sign it with sign_telegram_link_commitment. The link is minted
for the owner wallet, which is why the signature must recover that wallet.
{ "agent_id": 7 }Parameters
| Name | Type | Notes |
|---|---|---|
chain_id | integer | Optional. Absent, the server uses its first configured chain — which the shipped default makes Base Sepolia |
agent_id | integer | Required, in both steps |
nonce | string | Step 2. 0x-prefixed 32-byte hex, as returned by step 1 |
expiry | integer | Step 2. Unix seconds, one hour out |
agent_sig | string | Step 2. 0x-prefixed 65-byte hex |
Returns — step 1
| Field | Value |
|---|---|
status | commitment |
commitment, nonce, expiry | The typed data to sign, and its two echoed fields |
Returns — step 2
| Field | Value |
|---|---|
status | linked |
deep_link | The link the human taps to finish in Telegram |
expires_at | RFC3339. The deep link's expiry, not the commitment's |
This is the one backend-recovered commitment whose nonce we burn in our own database. Minting a deep link is not idempotent — a replayed signature could re-mint a link and reroute the owner's notifications — so the nonce is consumed before the mint, after the signature is verified. The canonical lowercase form is the key, so re-casing the echo does not slip past it.
Errors
| Message | Cause |
|---|---|
telegram linking not enabled | The link service is not wired on this deployment |
nonce and expiry are required with agent_sig | Step 2 missing an echoed field |
commitment expired | Past expiry. Restart at step 1 |
invalid nonce: must be 0x-prefixed 32-byte hex | Malformed nonce |
invalid agent_sig: must be 0x-prefixed 65-byte hex | Wrong signature length or not hex |
signature not from agent owner: recovered 0x…, want 0x… | The recovered signer is not ownerOf(agent_id) |
commitment nonce already used | Replay inside the window. Restart at step 1 |
agent N not found on-chain | ownerOf reverted |
mint telegram deep link | The mint itself failed after the nonce was burned — restart at step 1 |
get_pending_intents
Reads the intents the owner confirmed for this agent to execute, with the owner's remaining spend headroom.
Carries IntentAccessCommitment; sign it with sign_intent_access_commitment.
This commitment authorises a read. It names nothing but the agent, so a replay inside its one-hour window returns the queue again — whatever the queue holds by then. Treat a signed one as a short-lived read token, not a record of a decision. There is deliberately no nonce burn here: the operation is a pure read, so a replay achieves nothing the owner could not already do.
{ "agent_id": 7 }Parameters
| Name | Type | Notes |
|---|---|---|
chain_id | integer | Optional. Absent, the server uses its first configured chain — which the shipped default makes Base Sepolia |
agent_id | integer | Required, in both steps |
nonce, expiry, agent_sig | Step 2, as for link_telegram |
Returns — step 1
status: commitment, plus commitment, nonce and expiry.
Returns — step 2
| Field | Notes |
|---|---|
status | ok |
intents | The pending queue — see below |
remaining_budget | Remaining spend headroom in USDC base units, or null |
per_tx_cap_usdc | The owner's per-transaction ceiling, or null if unset |
budget_enforced | Whether a spending limit exists for this owner and chain |
Each intent carries intent_id, chain_id, agent_id, type (buy_product is the one that
spends money), payload, created_at and expires_at. payload is raw JSON to hand
straight to the executor tool. chain_id and agent_id are repeated on every intent so a runner
operating across several chains and identities can tell which one an intent targets.
Reading the budget correctly matters, and null is ambiguous by design.
remaining_budgetis the minimum over the caps that are set of (daily cap − spend since UTC midnight) and (total cap − all-time spend), clamped at zero.remaining_budget: nulldoes not mean unlimited. Checkbudget_enforcedto tell "no limit configured" from "a limit exists but no running cap was set" from "the read failed".remaining_budgetcan be null whilebudget_enforcedis true — when the owner set only a per-transaction cap. Check purely againstper_tx_cap_usdcin that case.- When
budget_enforcedis false, both figures are a no-op and the funded wallet balance is the only ceiling. - The backend enforces none of this. The numbers are advisory; a failed budget read is folded
into the same null and never fails the poll. Before executing a
buy_productintent whose amount would exceed either figure, skip it withmark_intent_skippedand tell the owner to raise the limit in the web console — the screen they do it on is in Settings and simulators.
One intent with an undecodable payload is skipped and logged rather than failing the whole poll, so a poll can return fewer intents than the queue holds.
Errors
| Message | Cause |
|---|---|
intent queue not enabled | The intent queue is not wired on this deployment |
nonce and expiry are required with agent_sig | Step 2 missing an echoed field |
commitment expired | Past expiry. Restart at step 1 |
invalid nonce: must be 0x-prefixed 32-byte hex | Malformed nonce |
invalid agent_sig: must be 0x-prefixed 65-byte hex | Wrong signature length or not hex |
signature not from agent owner: recovered 0x…, want 0x… | The recovered signer is not ownerOf(agent_id) |
agent N not found on-chain | ownerOf reverted |
get pending intents | The queue read failed. Transient; retry |
mark_intent_executed
Marks one polled intent executed, recording the result that was signed.
Carries IntentMarkCommitment; sign it with sign_intent_mark_commitment. intent_id is
required in both steps, and so is result if you intend to record one: both are bound into the
commitment, so the signature authorises exactly that intent, with exactly that result. Pass the
final result already in step 1.
{ "agent_id": 7, "intent_id": "itn_9f3c…", "result": "escrow:88" }Parameters
| Name | Type | Notes |
|---|---|---|
chain_id | integer | Optional. Absent, the server uses its first configured chain — which the shipped default makes Base Sepolia |
agent_id | integer | Required, in both steps |
intent_id | string | Required, in both steps |
result | string | Optional but signed. An escrow id, a transaction hash, or empty |
nonce, expiry, agent_sig | Step 2 |
Returns — step 1
status: commitment, plus commitment, nonce and expiry.
Returns — step 2
| Field | Notes |
|---|---|
status | ok |
marked | true when the flip happened; false when the intent was already terminal, unknown, or belongs to another agent |
marked: false is a success, not an error. The flip is agent-scoped in the store, so a replay
matches zero rows; that idempotence is why this commitment's nonce is not burned.
A result in step 2 that differs from the one signed in step 1 recovers a non-owner address and
is rejected.
Errors
| Message | Cause |
|---|---|
intent queue not enabled | The intent queue is not wired on this deployment |
intent_id is required | Omitted in step 1 |
intent_id is required with agent_sig | Omitted in step 2 |
nonce and expiry are required with agent_sig | Step 2 missing an echoed field |
commitment expired | Past expiry. Restart at step 1 |
invalid nonce: must be 0x-prefixed 32-byte hex | Malformed nonce |
invalid agent_sig: must be 0x-prefixed 65-byte hex | Wrong signature length or not hex |
signature not from agent owner: recovered 0x…, want 0x… | Not the owner — or a changed intent_id or result |
mark intent executed | The flip failed. Transient; retry |
mark_intent_skipped
Declines one polled intent, recording the reason that was signed.
The counterpart to mark_intent_executed, and the right call for an intent you evaluated and
chose not to carry out — over budget, terms drifted, product gone, policy decline. It clears the
intent immediately instead of leaving it to be re-polled until it expires.
Carries IntentSkipCommitment; sign it with sign_intent_skip_commitment. intent_id and
reason are both required in both steps: reason is bound into the commitment, so the owner
sees a reason nobody could change after signing.
{ "agent_id": 7, "intent_id": "itn_9f3c…", "reason": "live price above the confirmed cap" }Parameters
| Name | Type | Notes |
|---|---|---|
chain_id | integer | Optional. Absent, the server uses its first configured chain — which the shipped default makes Base Sepolia |
agent_id | integer | Required, in both steps |
intent_id | string | Required, in both steps |
reason | string | Required, in both steps, and signed |
nonce, expiry, agent_sig | Step 2 |
Returns — step 1
status: commitment, plus commitment, nonce and expiry.
Returns — step 2
| Field | Notes |
|---|---|
status | ok |
skipped | true when the flip happened; false when the intent was already terminal, unknown, or belongs to another agent |
Errors
| Message | Cause |
|---|---|
intent queue not enabled | The intent queue is not wired on this deployment |
intent_id is required | Omitted in either step |
reason is required | Omitted in step 1 |
reason is required with agent_sig | Omitted in step 2 |
nonce and expiry are required with agent_sig | Step 2 missing an echoed field |
commitment expired | Past expiry. Restart at step 1 |
invalid nonce: must be 0x-prefixed 32-byte hex | Malformed nonce |
invalid agent_sig: must be 0x-prefixed 65-byte hex | Wrong signature length or not hex |
signature not from agent owner: recovered 0x…, want 0x… | Not the owner — or a changed intent_id or reason |
mark intent skipped | The flip failed. Transient; retry |
Next steps
- Identity and commitments — what each of these four signatures binds
- Signer tools — the four signing tools that pair with these
- Money tools — the wallet balance a budget check should read alongside