Autonomy
How each environment applies change sets: auto, limits or confirm. What limits refuses (reward cost caps and nothing destructive), advisories, how an agent proposes and applies, how refusals and undo work, and who changes the policy.
Every configuration change an agent makes is a change set: a stored proposal with the exact plan of what it will do. Each environment has an autonomy mode that decides whether the proposer may apply it, or whether a person confirms it in the dashboard. Every applied change is logged with who applied it and under which policy, and most can be undone.
Modes
| Mode | Meaning |
|---|---|
auto | Any change set the caller may propose is applied on request. |
limits | Applied on request only when the plan is inside the limits below; otherwise it waits for a person. |
confirm | Applied only when a signed-in owner or admin confirms it in the dashboard. |
- A test environment is always
auto. It cannot be changed. - The live environment of an agent-created project starts in
limitsand stays there until a person claims the project. - The live environment of a project a person signed up for defaults to
confirm.
Read the policy of the current environment with GET /v1/autonomy_policy (tool
get_autonomy_policy): mode, effective_mode, limits, default_limits and configurable
(false in a test environment and in an unclaimed project).
What limits refuses
Orishare provides building blocks and does not judge your game or reward design: client-allowed
events, new currencies, experiments and your own rules apply in limits. The policy enforces
only two things, reward cost caps and nothing destructive. A change set is applied on request
unless its stored plan has any of these (each refusal names a stable reason code):
Reward cost caps
- a reward above the per-grant cap (
award_exceeds_cap), or an amount that is computed or cannot be read, so it has no stated bound (award_unbounded). This includes campaign, challenge and tier program operations, whose rewards the plan does not carry yet, and every item a rule, program, league, friend quest, friend streak or team quest grants (item.grant, a fixeditemreward) until the item's money value is read (item_value_unknown): a chest counts as the most its loot can pay, times the quantity; - activating an existing or older version (a rollback, a resume) whose rewards the plan does not
show (
activates_existing_version); - an approved bound above its cap: liability per currency, issuances, stock units, codes,
customers affected (
bound_exceeded).
Nothing destructive
- a deletion of any kind (
delete), or a change that removes, archives for good or rewrites operational data (destructive); - an irreversible change (
irreversible), such as publishing or revoking an offer version. Publishing a new rule, program or season version is not counted, also throughrule.publish_draftorrule.rollback(the old version stays in history and the head can move back), and neither is creating a new currency (an undo keeps it, unused); - a bulk operation that cannot be fully reversed (
bulk_not_reversible).
And, fail closed: a command whose cost and destructive metadata is not declared
(metadata_missing).
Advisories
The policy also notices things it does not block, and records them with the commit as
advisories in the stored decision: an event made client-allowed whose app-reported activity is
not capped (trust_weakening), a changed rule or program that client-allowed events reach
uncapped (client_uncapped), and an experiment that withholds rewards from part of the customers
(withholds_rewards). Each names what it found. Read them with
GET /v1/change_sets/{id}?include_advisories=true, in commit.policy.advisories. Every other
response leaves them out, including applications and undos (their answers are stored and replayed
for the same Idempotency-Key).
Economy changes
Economy changes (items, shop offers, energy, loot tables and battle passes) are held to the same
cost caps: in limits the cost of the change is read and they apply when what one grant can pay
is within the caps. Archiving is destructive. In a test environment they apply immediately, and on auto they
apply as any other change.
The cost reading: one grant is one purchase of an offer (its quantity of the item), one chest
opening (the number of rolls times the largest entry, per currency, plus every guaranteed
award), one battle pass tier track (free or premium), one energy refill tick, and one unit of an
item. Money per currency is held to max_award_per_grant and max_liability_per_currency;
currencies that are neither spendable nor transferable (XP) are not money and never count. A boost
multiplies every live rule and program award in its currency, and the result is held to
max_award_per_grant. Boosts that one change grants in the same currency run at the same time,
so their multipliers are multiplied together first (a 2x and a 3x boost on a 2,000 award pay
12,000), up to the runtime ceiling of 10. A rule that grants a boost item counts the same way. There is no separate bound on boost multipliers or durations: a
boost is held only through what it does to the awards it multiplies. (A stricter judging of
economy design, with a fixed boost bound, exists behind a switch that is off, so
boost_exceeds_bound is never returned.) An amount that cannot be
read, an unknown item or loot table, or a chest that pays itself is award_unbounded.
What the cap does not promise. The cap prices each change on its own; it is not a spending guarantee across the whole configuration, which would need a live budget at payout time. Three examples of what a change can still add up to with what is already there:
- a boost item that already exists multiplies a rule award that a later change adds (an existing 2x boost and a new rule awarding 6,000 pay 12,000 to a boosted customer);
- raising what a chest pays also raises every existing grant of that chest outside the change (a rule that grants two chests worth 4,000 each pays 12,000 once the chest pays 6,000);
- customers still on an older program version keep its rewards, which a new boost then multiplies (an older version paying 6,000 and a 2x boost pay 12,000).
Set the caps with these in mind, and confirm large changes yourself.
The default caps:
| Cap | Default |
|---|---|
max_award_per_grant | 10000 |
max_liability_per_currency | 1000000 |
max_issuances | 1000 |
max_customers_affected | 10000 |
max_stock_units | 1000 |
max_codes | 1000 |
Amounts are minor units of the currency, as decimal strings.
Propose, then apply
- Propose.
POST /v1/change_setswithcommands(see Author a mechanic for a complete example). The answer is the stored change set with itsplan, includingplan.plan_digest. Nothing is live yet. - Check the plan. Read
diff,effects,irreversibilityandflags; they are what the policy evaluates.GET /v1/change_sets/{id}reads it again later. - Apply.
POST /v1/change_set_applicationswith{ "change_set_id", "plan_digest" }and anIdempotency-Key. When the policy allows, the answer is201with the commit,status: "committed"andauthority: "policy".
const API = 'https://api.orishare.com';
const headers = {
Authorization: `Bearer ${process.env.ORISHARE_AGENT_TOKEN}`,
'X-Orishare-Environment': process.env.ORISHARE_TEST_ENV,
'Content-Type': 'application/json',
};
const proposeKey = crypto.randomUUID();
const changeSet = await (
await fetch(`${API}/v1/change_sets`, {
method: 'POST',
headers: { ...headers, 'Idempotency-Key': proposeKey },
body: JSON.stringify({ commands }),
})
).json();
const applyKey = crypto.randomUUID();
const res = await fetch(`${API}/v1/change_set_applications`, {
method: 'POST',
headers: { ...headers, 'Idempotency-Key': applyKey },
body: JSON.stringify({ change_set_id: changeSet.id, plan_digest: changeSet.plan.plan_digest }),
});Keep each key for its own request and send it again only when you repeat that same request.
What the commit returns
The 201 body is the commit: id (chc_...), change_set_id, status: "committed",
plan_digest, acknowledged_flags, change_set (the stored change set, now committed),
authority: "policy" when the policy applied it, and outcome. The shape of outcome depends on
the change set:
| Change set | outcome |
|---|---|
Configuration commands (config.apply_document, program.*, season.*, catalogue.*, offer.*, segment.create, ...) | { changes, published_rules, diff } |
One operation command (rule.*, economy_item.* and the other economy commands, currency.*, reward.*, league.*, notification_*, ...) | { operation, result, diff, destructive }: operation is the command type and result is what the matching direct route returns, for rule.create the created rule (its id is outcome.result.id) |
bulk.run | { bulk_operation_id, kind, item_count, pending, items_digest } |
For example, after applying rule.create, propose
{ "type": "rule.publish_draft", "key": "<outcome.result.id>", "expected_version": <outcome.result.version> }.
A change set a person confirmed in the dashboard does not hand you its outcome: GET /v1/change_sets/{id} shows only status and commit (who confirmed it and when). Find what it
created by its name or key in the resource's list (GET /v1/rules, GET /v1/programs, ...).
The apply runs the same commit as a dashboard confirmation: it plans again under locks, checks the
digest, the bounds and the expiry, and records everything atomically. The credential must hold the
plan's required_scopes now. A change set expires 24 hours after it was proposed.
Refusals
When the policy does not allow the change, the apply answers 403 confirmation_required and the
change set stays pending:
{
"error": {
"code": "confirmation_required",
"message": "This change set is outside the autonomy limits; a person confirms it in the dashboard",
"details": {
"mode": "limits",
"reasons": [{ "code": "delete", "message": "The change deletes a resource" }],
"review_url": "https://app.orishare.com/change-sets/chs_..."
}
}
}details.reasons lists every reason with its code and message, and details.review_url is the
dashboard page where a person confirms the change set. Do not retry it. Either change the proposal so it fits the limits (for example a smaller award),
or give details.review_url to your person with one sentence on what the change does and why it
needs them. In confirm mode every apply is refused with the reason confirmation_mode. Then
read GET /v1/change_sets/{id} every 10 to 30 seconds until status is no longer pending.
A direct value-configuration call from an agent credential in a live environment (not auto) also
answers confirmation_required, with details.reason ai_gate: propose it as a change set
instead. See the agent gate.
A person who signed up with POST /v1/auth/signup (the CLI) and has not verified the email yet is
held the same way in a live environment: direct configuration writes answer 403 confirmation_required
with details.reason email_unverified until they sign in once at the dashboard with that
address (magic link or Google). Test environments and organization management are not affected.
Undo
POST /v1/change_set_undos with { "change_set_id" } undoes a committed change set. An undo is a
configuration rollback: it proposes a new change set that restores the configuration the first
one changed (what it created is archived or deactivated, never deleted) and applies it under the
same policy. Pausing, resuming and activating a league, friend quest, friend streak, team quest or
notification message type is undone by the opposite command.
The answer is { change_set, application, residuals }; application is null when the undo
needs a person, and the undo then waits as a pending change set. residuals lists what an undo
cannot take back, one { kind, detail } per kind:
kind | What stays |
|---|---|
currency_kept | A currency the change created stays, unused (currencies are never deleted). |
ledger_entries_settled | Awards customers already received while the change was live. |
progress_recorded | Progress, enrollments, ranks and claims recorded meanwhile. |
items_issued | Rewards and items already issued, bought or consumed. |
codes_delivered | Codes and gift cards already delivered stay valid. |
notifications_sent | Notifications already sent. |
experiment_exposure | Customers already assigned to a variant keep what it gave them. |
An irreversible change, an operation command without an inverse, a bulk operation or a playbook
(they have their own undo), or an item changed again since, has no undo and answers
invalid_state.
The activity log
Every applied change set records its authority (policy or confirmation), the principal that
applied it and the policy decision. GET /v1/change_sets?authority=policy lists what was applied
under the policy.
In the dashboard (https://app.orishare.com):
- Settings, Agent autonomy (
/settings/autonomy): the mode and the caps of the live environment. Only an owner or admin signed in to the dashboard changes them (it usesPATCH /v1/autonomy_policywith the dashboard session); no API key, token, MCP tool or assistant can. - Operations, Agent activity (
/agent-activity): every change applied under the policy, with Undo. - Changes (
/changes/{id}): the review page where a person confirms a pending change set. The links the API returns (review_url,dashboard_url) are<dashboard>/change-sets/{id}: that address finds the change set's environment and opens this page. - Approvals (
/approvals/{id}): the review page of a pending action (one exact request an AI asked a person to approve); itsreview_urlpoints there.
Claim
An agent-created project has no human owner until someone opens its claim link
(https://app.orishare.com/claim#occ_...), signs in with a verified email and confirms. That
person becomes the owner and the agent user becomes an admin. Nothing else changes. After the
claim the owner may choose any mode, and may revoke the agent's token and keys.
Connect your AI
Connect Claude, ChatGPT, Cursor, VS Code, Codex or any MCP client to Orishare: one-click sign-in with the connector URL https://api.orishare.com/mcp/connect, or an AI access key (opat_) on https://api.orishare.com/mcp. Setup per client, what the AI may do, and the OAuth details for AI app developers.
Orishare
Loyalty, incentives, promotions and engagement infrastructure behind one REST API.