Orishare

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:

  1. 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.
  2. Connect MCP to configure the project with tools, or keep using plain HTTP.
  3. 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:

FieldWhat it is
organizationid, name, slug, claimed: false
projectid, name
environments[]One test (runtime_class sandbox) and one live (production): id, name, runtime_class, active, autonomy_mode, allowed_origins
environments[].secret_keyid, token (osk_test_... or osk_live_...), scopes. Server side only
environments[].publishable_keyid, token (opk_...). Safe in frontend code
agentuser_id, token_id, token (opat_..., scopes *): your management credential and MCP credential
claimcode, url, owner_email: the link your person opens to own the project
limitsproduction_events_per_month (0), live_requires_claim (true) and orishare_sent_messages (0) until claimed
linksdocs, 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 example ORISHARE_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.

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 201 with status: "pending", and POST /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, then POST /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}/reset to start over). The live environment exists with its keys, but billable work there (events, ledger adjustments, reward and program commands) answers live_requires_claim until 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 suppressed with reason project_unclaimed until 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

On this page