Orishare

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

ModeMeaning
autoAny change set the caller may propose is applied on request.
limitsApplied on request only when the plan is inside the limits below; otherwise it waits for a person.
confirmApplied 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 limits and 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 fixed item reward) 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 through rule.publish_draft or rule.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:

CapDefault
max_award_per_grant10000
max_liability_per_currency1000000
max_issuances1000
max_customers_affected10000
max_stock_units1000
max_codes1000

Amounts are minor units of the currency, as decimal strings.

Propose, then apply

  1. Propose. POST /v1/change_sets with commands (see Author a mechanic for a complete example). The answer is the stored change set with its plan, including plan.plan_digest. Nothing is live yet.
  2. Check the plan. Read diff, effects, irreversibility and flags; they are what the policy evaluates. GET /v1/change_sets/{id} reads it again later.
  3. Apply. POST /v1/change_set_applications with { "change_set_id", "plan_digest" } and an Idempotency-Key. When the policy allows, the answer is 201 with the commit, status: "committed" and authority: "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 setoutcome
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:

kindWhat stays
currency_keptA currency the change created stays, unused (currencies are never deleted).
ledger_entries_settledAwards customers already received while the change was live.
progress_recordedProgress, enrollments, ranks and claims recorded meanwhile.
items_issuedRewards and items already issued, bought or consumed.
codes_deliveredCodes and gift cards already delivered stay valid.
notifications_sentNotifications already sent.
experiment_exposureCustomers 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 uses PATCH /v1/autonomy_policy with 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); its review_url points 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.

On this page