Lifecycle & autonomy
An agent isn’t a fire-and-forget script. It has a lifecycle the owner controls: it can be paused and resumed, it can park an operation until you approve it, and it can be revoked for good. Every transition except the agent’s own actions is owner-only, and every transition is written to the agent’s event log.
The states
Section titled “The states”| State | Meaning |
|---|---|
pending |
Created but not yet funded. An XRPL agent skips this state: its account is funded when it is created, so it starts active. |
active |
Funded and operational. The only state in which the agent can act. |
paused |
Halted by the owner. The state in which you can change the rules config. |
awaiting_approval |
An operation was above the owner-approval threshold and is parked until you approve or reject it. |
revoked |
Permanently disabled. You can then delete it. |
stopped |
Listed in the status type. Nothing sets it today. |
The state machine
Section titled “The state machine”
stateDiagram-v2
[*] --> active: two-phase create (XRPL)
[*] --> pending: synchronous create
pending --> active: fund() (first funding)
active --> paused: pause() (owner)
paused --> active: resume() (owner)
active --> awaiting_approval: op above approvalThreshold
awaiting_approval --> active: approve() / reject() (owner)
paused --> paused: updateRules() (owner)
active --> revoked: revoke() (owner)
paused --> revoked: revoke() (owner)
awaiting_approval --> revoked: revoke() (owner)
revoked --> [*]: delete() (owner)
The platform enforces these rules:
- An XRPL agent is
activewhen it is created. The two-phase create funds its account, so there is no separate activation. On a synchronous create, the firstfundmoves the agent frompendingtoactive. - Acting requires
active. A capability run on an agent in any other state is refused. - The rules config changes only while
paused.updateRulesis refused otherwise. A rules key that starts with$, contains., or is__proto__,constructororprototypeis refused with400, at creation as well, because the agent’s record is replicated to the other clusters and they refuse such keys. - Revoke is final and owner-only. Revoking again answers
already revoked. - Status changes reach every cluster, and a revoke always wins. Each cluster applies the newest status it receives. A
resumemade on a cluster the revoke has not reached yet is overridden when the revoke arrives, and everyresumeafter that fails. If you need a stop that holds on every cluster, revoke. - Delete hides the agent; it does not free its slot.
deleterevokes the agent if needed and hides it from every read; a deleted id answers404. The agent keeps its slot in your agent limit until its reserve is reclaimed. Delete does not touch the chain: the agent’s wallet entity stays listed under its owner, and its funds move only as far as its rulebook allows. - Reclaiming the reserve frees the slot. After a delete,
baas.agents.reclaimReserve(agentId)closes the XRPL account and sends its whole balance to whoever funded it, read from the ledger, never from the request. The slot is released once, after the ledger confirms the account is gone. See Identity & wallets. - Approve and reject resolve the parked operation.
approvechecks the operation against the rulebook once more before releasing it and returns the agent toactive.rejectdiscards it and returns the agent toactive.
Who can trigger what
Section titled “Who can trigger what”| Operation | Caller | Resulting transition |
|---|---|---|
| two-phase create (XRPL) | Owner (payer) | → active |
| synchronous create | Owner | → pending |
fund |
Owner | pending → active (first time) |
| capability run | The agent, through its triggers | stays active, or → awaiting_approval |
withdraw |
Owner | none |
pause / resume |
Owner | active ↔ paused |
updateRules |
Owner | none (must be paused) |
approve / reject |
Owner | awaiting_approval → active |
revoke |
Owner | → revoked |
delete |
Owner | → revoked, then hidden from every read |
reclaimReserve |
Owner | none (after delete) |
The agent does exactly one class of thing: run its own declared capabilities. A capability run is accepted only from the agent’s own wallet, and no person holds that wallet’s key, so in practice the agent acts through its triggers. A call to execute or trade from your app or your owner wallet is refused with 403. Everything else is the owner’s.
Creating an agent is gated too. Each agent counts against the app’s agent limit and, for an agent created for a customer, that customer’s limit, checked before the DKG runs. Past a limit the create is refused with 400. See Deploy your first agent (Step 2) for the defaults.
Autonomous mode
Section titled “Autonomous mode”“Autonomous” doesn’t mean “unsupervised by rules”. It means no human in the request loop for routine operations. The agent acts on triggers you put in its rules config, and every action ends at the same fail-closed rule check.
- Schedule triggers fire a capability every
intervalMsmilliseconds. The runner polls every 30 seconds by default and reads the triggers fresh on each poll. The schedule is kept in memory: after a restart every schedule starts counting again from that moment. It is best-effort timing, not a durable scheduler. - Event triggers fire a capability on each message on a subject inside the agent’s own app namespace:
app.<appId>orapp.<appId>.<anything>. Besides your app’s own events, the platform publishes your app’s deploy, billing, data-write and messaging events there; the subjects are listed in Deploy your first agent. Event triggers are subscribed when the platform host starts, so a new or changed event trigger takes effect after the host’s next restart.
Only an agent that belongs to a Smart App fires triggers. A trigger fires only a capability that is both declared on the agent and named in its triggers. A trigger cannot name a chain, so on-chain capabilities (trade, payout, token) are fired through Path A.
Path A — a function of your app decides
Section titled “Path A — a function of your app decides”- A schedule or event trigger fires
function-invokewith your function’s id. - Your function runs as the agent, metered to the agent, and decides what to do.
- It returns a decision and up to eight
intents, each naming a capability, a chain and params. - Each intent is checked like a direct action: declared capability, then the rulebook, before any validator signs.
Submitting the resulting signed transaction to the ledger from a trigger is planned. Until then, on-chain intents are checked and signed but have no on-chain effect.
Path B — the agent’s own trigger runtime (no HTTP call)
Section titled “Path B — the agent’s own trigger runtime (no HTTP call)”A trigger can also fire an off-chain capability directly, with no function and no HTTP call:
// on the agent's rulesConfigtriggers: { schedule: [{ capability: 'read', intervalMs: 60_000, params: { url: 'https://api.example.com/price' } }], events: [{ capability: 'notify', subject: 'app.<appId>.deposit', params: { url: 'https://hooks.example.com/x', message: 'got a deposit' } }],}Every URL must be on the agent’s offchain.allowedUrls. An event subject that names a platform channel, a bare wildcard such as > or app.*.>, or another app is refused when you set the rules and again when the agent subscribes, so an agent can only be woken by its own app’s events.
Both paths converge on the same check:
sequenceDiagram
participant Trig as Trigger (schedule or event)
participant Fn as Your function (Path A)
participant Agent as Agent capability run
participant Gate as Rule check (fail-closed)
Trig->>Fn: function-invoke
Fn-->>Agent: intents (capability, chain, params)
Trig->>Agent: off-chain capability (Path B)
Agent->>Gate: check
alt within the rules
Gate-->>Agent: signed by the validators
else above the approval threshold
Gate-->>Agent: parked → awaiting_approval
else breaks a rule
Gate-->>Agent: denied, nothing signed
end
Whatever decides, a function or a model it calls, has no signing authority. The worst a compromised or hallucinating decision can do is propose something that gets denied. For large moves, approvalThreshold brings you back into the loop by parking the operation in awaiting_approval.