Start here for AI agents
Three steps for an AI agent adding rewards to an app: sign up with no browser, connect MCP, pick a recipe. Exact HTTP calls, the proof of work, what to store and what to hand your person.
You are an AI agent and your person asked you to add rewards, points, streaks, quests or a loyalty program to their app. You can do all of it yourself, with no browser and no human in the loop:
- Sign up with two HTTP calls (or one MCP tool). You get a project with one test and one live environment, keys, an agent token and a claim link.
- Connect MCP to configure the project with tools, or keep using plain HTTP.
- Pick a recipe for your stack and follow it.
Every page of these docs is also plain markdown: add .md to its URL
(https://docs.orishare.com/docs/agent-start.md). https://docs.orishare.com/llms.txt lists every
page and https://docs.orishare.com/llms-full.txt holds all of them in one file.
Base URL. The API origin is https://api.orishare.com and every REST route is under /v1.
The examples keep the origin in a constant, const API = 'https://api.orishare.com', and write
full paths after it (API + '/v1/agent/signups').
1. Sign up with no browser
Signup is public and needs no account. To keep it open without a CAPTCHA, it asks for a small proof of work: your machine spends about a second hashing.
Step 1: get a challenge
POST /v1/agent/signup_challenges with an empty JSON body returns a single-use challenge, valid
10 minutes:
curl -s -X POST https://api.orishare.com/v1/agent/signup_challenges \
-H 'Content-Type: application/json' -d '{}'{
"challenge": "sgc_00000000000000000000000000000001.c2FsdHNhbHRzYWx0c2FsdHNhbHQ",
"algorithm": "sha256-leading-zero-bits",
"difficulty": 18,
"expires_at": "2026-10-04T00:10:00.000000Z"
}Step 2: solve the proof of work
Find a nonce (1 to 64 letters or digits) such that sha256(challenge + ":" + nonce) starts
with at least difficulty zero bits. Counting up from 0 works. Use the difficulty the
challenge returned (18 for the first signup from a network, up to 26).
Node:
node -e 'const c=require("crypto"),[ch,d]=process.argv.slice(1);for(let n=0;;n++){const h=c.createHash("sha256").update(ch+":"+n).digest();let b=0;for(const x of h){if(!x){b+=8;continue}b+=Math.clz32(x)-24;break}if(b>=+d){console.log(n);break}}' "<challenge>" <difficulty>Python:
python3 -c 'import hashlib,sys;ch,d=sys.argv[1],int(sys.argv[2]);n=0
while int.from_bytes(hashlib.sha256(f"{ch}:{n}".encode()).digest(),"big")>>(256-d):n+=1
print(n)' "<challenge>" <difficulty>The output is the nonce.
Step 3: create the project
POST /v1/agent/signups with the challenge, the nonce and an Idempotency-Key. Generate the key
once for this signup and send the same key if you repeat the request after a lost response.
curl -s -X POST https://api.orishare.com/v1/agent/signups \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: signup-space-miner-1' \
-d '{
"project_name": "Space Miner",
"allowed_origins": ["https://spaceminer.example", "http://localhost:5173"],
"proof": { "challenge": "<challenge>", "nonce": "<nonce>" }
}'Body fields: project_name (required, shown to the person who claims the project), timezone
(IANA, default UTC), allowed_origins (exact browser origins of the app, at most 20: the
test environment gets all of them, the live one only the https:// ones), owner_email (optional: only this verified email can claim the project) and
agent ({ "name": "..." }, optional, for the log).
The same three steps in one Node script (Node 18 or later, no packages):
import { createHash } from 'node:crypto';
const API = 'https://api.orishare.com';
const json = { 'Content-Type': 'application/json' };
const challengeRes = await fetch(`${API}/v1/agent/signup_challenges`, {
method: 'POST',
headers: json,
body: '{}',
});
const { challenge, difficulty } = await challengeRes.json();
const zeroBits = (digest) => {
let bits = 0;
for (const byte of digest) {
if (byte === 0) {
bits += 8;
continue;
}
return bits + Math.clz32(byte) - 24;
}
return bits;
};
let n = 0;
while (zeroBits(createHash('sha256').update(`${challenge}:${n}`).digest()) < difficulty) n += 1;
// One key for this signup; reuse it if you send the same signup again.
const signupKey = crypto.randomUUID();
const signupRes = await fetch(`${API}/v1/agent/signups`, {
method: 'POST',
headers: { ...json, 'Idempotency-Key': signupKey },
body: JSON.stringify({
project_name: 'Space Miner',
allowed_origins: ['https://spaceminer.example', 'http://localhost:5173'],
proof: { challenge, nonce: String(n) },
}),
});
const signup = await signupRes.json();
console.log(JSON.stringify(signup, null, 2));The same in Python (standard library only):
import hashlib, json, urllib.request, uuid
API = "https://api.orishare.com"
def call(path, body, headers=None):
req = urllib.request.Request(
API + path, data=json.dumps(body).encode(), method="POST",
headers={"Content-Type": "application/json", **(headers or {})})
with urllib.request.urlopen(req) as res:
return json.load(res)
c = call("/v1/agent/signup_challenges", {})
n = 0
while int.from_bytes(hashlib.sha256(f"{c['challenge']}:{n}".encode()).digest(), "big") >> (256 - c["difficulty"]):
n += 1
signup_key = str(uuid.uuid4()) # one key for this signup; reuse it on a repeat
signup = call("/v1/agent/signups", {
"project_name": "Space Miner",
"allowed_origins": ["https://spaceminer.example"],
"proof": {"challenge": c["challenge"], "nonce": str(n)},
}, {"Idempotency-Key": signup_key})
print(json.dumps(signup, indent=2))Errors: 422 proof_of_work_invalid (wrong, used or expired challenge: get a new one), 429 rate_limited (wait for Retry-After; signups are limited per address and network).
What the signup returns
201 with:
| Field | What it is |
|---|---|
organization | id, name, slug, claimed: false |
project | id, name |
environments[] | One test (runtime_class sandbox) and one live (production): id, name, runtime_class, active, autonomy_mode, allowed_origins |
environments[].secret_key | id, token (osk_test_... or osk_live_...), scopes. Server side only |
environments[].publishable_key | id, token (opk_...). Safe in frontend code |
agent | user_id, token_id, token (opat_..., scopes *): your management credential and MCP credential |
claim | code, url, owner_email: the link your person opens to own the project |
limits | production_events_per_month (0), live_requires_claim (true) and orishare_sent_messages (0) until claimed |
links | docs, openapi, dashboard, mcp |
Both secret keys carry the same scopes, including minting customer sessions
(customer_sessions:write). In a live environment, what an agent's key may write directly is limited by
the environment's autonomy policy: events, customers, sessions and other runtime calls work, and
configuration goes through change sets; see Autonomy.
Store the secrets
The tokens are shown once. A repeat of the same signup with the same Idempotency-Key returns
the project with every token, the claim code and the claim URL set to null.
- Put the secret keys and the agent token in the app's server environment variables or a secrets
file the app does not commit (add it to
.gitignore), for exampleORISHARE_SECRET_KEY,ORISHARE_AGENT_TOKEN,ORISHARE_TEST_ENV,ORISHARE_LIVE_ENV. - Never put a secret key (
osk_) or the agent token (opat_) in frontend code, a mobile app, a URL or a log. The publishable key (opk_) is meant for the browser and the app. - If you lose the agent token before anything is stored, sign up again with a new
Idempotency-Key: the first project stays unclaimed and unused.
Hand the claim link to your person
Tell your person, in plain words: "Open this link to own the Orishare project I created:
<claim.url>". The link looks like https://app.orishare.com/claim#occ_.... They sign in with a
verified email (magic link or Google) and press claim. Nothing stops working: keys, environments
and configuration stay as they are. If you set owner_email, only that address can claim. To
replace a lost link, call POST /v1/agent/claim_codes with the agent token (unclaimed projects
only).
2. Connect MCP
The hosted MCP server is https://api.orishare.com/mcp (streamable HTTP). With Claude Code:
claude mcp add --transport http orishare https://api.orishare.com/mcp \
--header "Authorization: Bearer <agent token>" \
--header "X-Orishare-Environment: <test environment id>"With a secret key (osk_) instead, leave out X-Orishare-Environment: the key is bound to its
environment. Other clients, the signup tools you get with no credential, and the local stdio
server are on Connect MCP. MCP is optional: every tool is a REST call in
openapi.json.
3. Pick a recipe
Recipes are step-by-step integrations for one stack: what to configure, what code to add and how to test it. Until your stack has one, use the integration prompts and the agent integration guide.
Test and live environments
Environments come in two kinds, test (runtime_class: sandbox) and live
(runtime_class: production), with free-form names; a project starts with one of each and can add
more (Environments and limits).
- A test environment applies every change set you propose: a proposal answers
201withstatus: "pending", andPOST /v1/change_set_applications{ "change_set_id", "plan_digest" }always commits it there. Build and test everything there. - A live environment follows its autonomy policy: read it with
GET /v1/autonomy_policy. - An agent-created project starts its live environment in
limits: you apply your own change sets, and only rewards above the cost caps (or with no readable amount), deletes, destructive or irreversible changes and commands without declared metadata wait for a person. Weaker event trust is recorded as an advisory, not refused. - In a live environment, configure through change sets:
POST /v1/change_sets, thenPOST /v1/change_set_applications. - Events, customers, keys, origins, webhooks and the widget are direct calls in every environment.
- Only a person signed in to the dashboard changes the policy, and only after claiming.
Limits until claimed
An unclaimed project works for as long as it is used, inside these ceilings:
- Test environments only. Build and test everything in the test environment now (free, 25
test players, reset it with
POST /v1/environments/{id}/resetto start over). The live environment exists with its keys, but billable work there (events, ledger adjustments, reward and program commands) answerslive_requires_claimuntil a verified person claims the project. After the claim, the organization's free allowance applies: 10,000 live actions a month, then $0.25 per 1,000 with a payment method. See Environments and limits. - No notification email or push sent by Orishare for the project until a person claims it
(the sign-in link email to the person is separate). Built-in email and push from the live environment
messages are recorded as
suppressedwith reasonproject_unclaimeduntil the claim; connectors to your own tools (Klaviyo, Braze, OneSignal, FCM) and webhooks to your own servers still run. Claiming allows sends from then on; it never replays a message suppressed before. - No billing starts without a human owner.
Next
- Connect MCP: hosted and local setup, the signup tools, what each tool may do.
- Connect your AI: once your person owns the project, they can connect their own AI app with one-click sign-in or an AI access key.
- Autonomy: modes, what
limitsrefuses, propose then apply, undo. - Recipes: integrations by stack.
- Agent integration guide: credentials, retries, events, customer sessions.
- Author a mechanic: a complete change set for a streak.
- OpenAPI and capabilities.json: the only lists of routes and tools.