oc · me
§ integrate · quickstart

Nine steps. About five minutes to first envelope; ten more to harden for production.

From new project to first billable envelope, then a production-hardening HMAC step on top — every step has the actual code sample. Mark steps done as you go; progress persists locally, no account needed to read this page.

§ progress
0 / 9 · 0%
  1. [01]

    create your project

    sign in at me.ochk.io, then run the three-step wizard at /me/projects/new. your OC identity IS your integrator account — no separate developer signup. you get a project_key + a public JWK URL. ~60 seconds.

    # /me/projects/new
    display_name: YourCompany
    domain: yourcompany.com
    mode: test
    → project_key: pk_test_a4f9k2x...
    open /me/projects/new →
  2. [02]

    verify your domain

    until verified, your project_key cannot fire live envelopes against your domain. add a DNS TXT record (or a meta tag on your homepage) and click "verify now" — backend tries DNS first, falls back to the meta tag fetch. test mode is unrestricted; this gate applies to live mode only.

    # DNS TXT at yourcompany.com
    oc-verify=<token shown on /me/projects/[id]>
    
    # OR meta tag in <head>
    <meta name="oc-domain-verify" content="<token>" />
    see the verify card →
  3. [03]

    install the SDK

    one npm package, three subpath entry points. /server has the framework adapters (no env vars, no JWK handling). /popup has the signin orchestrator. the main entry has the event, payment and session calls. the React provider (OcSessionProvider, useOcSession) comes from @orangecheck/auth-client.

    yarn add @orangecheck/me-client @orangecheck/auth-client
  4. [04]

    drop the popup signin

    add a button. inside the click handler, call signInWithOc(). a 480x720 me.ochk.io popup opens, user signs in (email-OTP or BIP-322), popup postMessages a token bound to your origin back, closes itself. OC's cookie lives on ochk.io, so a site outside the family carries the token itself: as a Bearer to your backend, or traded once for your own cookie (the Next.js stack snippets do that). a *.ochk.io site already has the family cookie and skips this.

    import { setBearerToken } from '@orangecheck/me-client';
    import { signInWithOc } from '@orangecheck/me-client/popup';
    
    button.addEventListener('click', async () => {
      const result = await signInWithOc();
      if (!result) return; // user cancelled
      setBearerToken(result.token); // me-client calls ride it
      const me = await fetch('/api/auth/me', {
        headers: { Authorization: `Bearer ${result.token}` },
      });
    });
  5. [05]

    verify the session server-side

    wrap your protected route with withOcAuth (Next.js Pages), ocAuthExpress (Express), or ocAuthHono (Hono). the wrapper handles JWKS retrieval + caching — no env vars. pass audience as your own origin: the popup issues your site a token bound to it, and a token issued to any other site is refused. a *.ochk.io site reads the family cookie and leaves audience out. answer /api/auth/me as { ok: true, account: { id, did_oc } } — the shape OcSessionProvider reads.

    // pages/api/auth/me.ts
    import { withOcAuth } from '@orangecheck/me-client/server';
    
    export default withOcAuth(async (req, res) => {
      if (!req.ocSession) return res.status(401).json({ ok: false });
      res.status(200).json({
        ok: true,
        account: { id: req.ocSession.sub, did_oc: req.ocSession.did_oc },
      });
    }, { audience: 'https://your-site.example' });
    see all stack snippets →
  6. [06]

    fire a billable event from your backend

    sign the request with your project secret and name who earns inside the signed body. no browser, no user cookie, and no reason for your backend to hold your users' session tokens — the signature is you asserting this identity did this on your site. store their did_oc next to your own user row once and you never need their session again.

    // server-side · the earner rides inside the bytes you sign
    import { oc } from '@orangecheck/me-client';
    
    await oc.event.fire({
      project_key: process.env.OC_PROJECT_KEY!,
      subtype: 'session_creation', // any subtype your project enabled
      signingSecret: process.env.OC_SIGNING_SECRET!, // never in a browser
      userAddress: user.did_oc,                     // who earns
      idempotencyKey: crypto.randomUUID(),
    });
    reveal the signing secret →
  7. [07]

    smoke-test the loop

    fire a real envelope under your project_key from /me/projects/[id] (smoke test panel). see it land in the live event stream within seconds. test webhook delivery with one click before you wire your backend. exact same pipeline as production — signed, co-signed, OTS-anchored, fanned out.

    # /me/projects/[id]
    [ fire test envelope → ]
      subtype: session_creation
      ↓
      envelope id: oc-me-tx7m4f9k
      webhooks_fired: 0 (none registered yet)
      → live in /verify/[id]
    see the user demo end-to-end →
  8. [08]

    flip to live

    set per-event prices in the config editor. choose how much of each fee streams to your user vs your rebate. domain verified + project in live mode = envelopes ship. paired comparisons against your actual stack come back in a business day if you want one.

    # /me/projects/[id] · config
    mode: live
    session_creation:    60 sats · user_share 65%
    payment_authorization: 0.75% of amount · user_share 65%
    request a paired comparison →
  9. [09]

    the signature, by hand

    the SDK does this for you — this is the wire format if you are not using it. signing is NOT optional: a user session proves a user, never the paying project, so an unsigned POST is refused in test mode as well as live. the one exception is a request from the project owner's own session, which is the CLI test-fire path and not a shape a backend should rely on. reveal or rotate the 256-bit secret in /me/projects/[id]/keys.

    // node · sign every /api/integrator/event POST
    import { createHmac } from 'node:crypto';
    const ts = Math.floor(Date.now() / 1000);
    const sig = createHmac('sha256', process.env.OC_SIGNING_SECRET)
      .update(`${ts}.${rawBody}`)
      .digest('hex');
    headers['X-OC-Signature'] = `t=${ts},v1=${sig}`;
    reveal/rotate the secret →

next, deeper

The quickstart gets you to first envelope. After that: /why#integrators has the full pitch + comparison table, /integrate/sandbox runs your config against a representative one-month event mix, and docs.ochk.io/me/sdk documents every export, type signature, and code sample.

Just want your users to have an OrangeCheck identity — not the me.ochk.io billable-events product? That is a separate, simpler integration: docs.ochk.io/integration covers letting OrangeCheck be your sign-in, or adding a did:oc to the login you already run.