live · mainnetme · ochk · io
federation-custodied · self-custody-ready
§ sdk · reference

@orangecheck/me-client v0.5.0

Every export from the consumer SDK, with type signatures and code samples. The SDK is the canonical integration path; the underlying API endpoints are documented at /developer/docs.

§ install

Two npm packages. me-client is the consumer SDK; auth-client is the React provider + hook (re-exported by me-client for convenience).

# yarn (recommended)
yarn add @orangecheck/me-client @orangecheck/auth-client

# or npm
npm i @orangecheck/me-client @orangecheck/auth-client
§ session provider · React

Mount OcSessionProvider once at the root. Every component below it can call useOcSession() to read the live cross-subdomain session.

  • OcSessionProvider <OcSessionProvider config?={OcAuthConfig}>
    Default authOrigin is https://ochk.io. Override only in dev/staging.
// _app.tsx (Next.js Pages router) — wrap your app once.
import { OcSessionProvider } from '@orangecheck/me-client';

export default function App({ Component, pageProps }) {
  return (
    <OcSessionProvider>
      <Component {...pageProps} />
    </OcSessionProvider>
  );
}
§ useOcSession · React

Returns { status, account, signInUrl, signOut, refresh, error }. Status is loading | authenticated | anonymous | error. Account is null when not authenticated.

  • useOcSession() () => OcSessionState
    Throws if called outside an OcSessionProvider.
  • useOptionalOcSession() () => OcSessionState | null
    Returns null instead of throwing.
// any component
import { useOcSession } from '@orangecheck/me-client';

function Header() {
  const { status, account, signInUrl, signOut } = useOcSession();

  if (status === 'loading') return <Skeleton />;
  if (status !== 'authenticated') return <a href={signInUrl}>sign in</a>;

  return (
    <div>
      signed in as {account.address.slice(0, 8)}…
      <button onClick={() => signOut()}>sign out</button>
    </div>
  );
}
§ oc.session · session lifecycle

Class C billable atom — sites pay per session, not per click. create() opens a new session; refresh() and invalidate() are free, telemetry-only.

  • oc.session.create(opts) (SignInOptions) => Promise<Session>
    Class C billable.
  • oc.session.refresh(id) (string) => Promise<Session>
    Free.
  • oc.session.invalidate(id) (string) => Promise<void>
    Free.
import { oc } from '@orangecheck/me-client';

const session = await oc.session.create({
  scope: ['identity', 'payment'],
  sessionPolicy: { duration_seconds: 7 * 86400, refresh: 'sliding' },
});

// refresh inside the window — free, telemetry only
await oc.session.refresh(session.id);

// invalidate when the user signs out — free
await oc.session.invalidate(session.id);
§ oc.payment.authorize

Class B billable atom — sites pay a configured percentage of the underlying payment amount. The user is prompted by me.ochk.io to consent before this resolves.

  • oc.payment.authorize(opts) (PaymentAuthorizeOptions) => Promise<PaymentResult>
    Class B billable; cashback streams to the user.
import { oc } from '@orangecheck/me-client';

const result = await oc.payment.authorize({
  identity: 'bc1q...',
  amount_sats: 240_000,
  description: 'breez · march invoice',
});
// result.id, result.user_envelope_id, result.verify_url
// Class B billable event for your site; cashback streams to the user.
§ oc.config · IntegratorPriceConfig

Get and update your IntegratorPriceConfig. validate() runs the same validator the server uses, locally — useful for live form feedback without a round-trip.

  • oc.config.get() () => Promise<IntegratorPriceConfig>
  • oc.config.update(cfg) (IntegratorPriceConfig) => Promise<IntegratorPriceConfig>
    Throws on local invalidity; 422 on server rejection.
  • oc.config.validate(cfg) (IntegratorPriceConfig) => ValidationResult
    No network call.
import { oc, validateIntegratorConfig } from '@orangecheck/me-client';

// Read your live config
const cfg = await oc.config.get();

// Mutate it
await oc.config.update({
  ...cfg,
  events: {
    ...cfg.events,
    payment_authorization: {
      enabled: true,
      site_pays: { kind: 'percent_of_amount', pct: 0.0085 },
      user_share_pct: 0.65,
    },
  },
});

// Validate without a round-trip
const result = validateIntegratorConfig(cfg);
if (!result.ok) console.error(result.errors);
§ oc.webhook.verify

Verify OC webhook signatures against the published JWKS. ALWAYS pass the raw body bytes (not the parsed JSON) — frameworks that re-serialize before your handler will produce a different byte sequence and the signature will not validate.

  • oc.webhook.verify(rawBody, sigHex, kid, jwk?) (string|Uint8Array, string, string, OcPublicJwk?) => Promise<VerifyResult>
    Auto-fetches and caches ochk.io/.well-known/jwks.json for 1h when jwk is omitted.
  • oc.webhook.fetchJwks() () => Promise<OcPublicJwk[]>
// node · express raw-body verification
import { oc } from '@orangecheck/me-client';

app.use(express.text({ type: 'application/json' }));

app.post('/api/oc/webhook', async (req, res) => {
  const sig = req.header('OC-Signature');
  const kid = req.header('OC-Key-Id');

  const result = await oc.webhook.verify(req.body, sig, kid);
  if (!result.ok) return res.status(401).end(result.reason);

  const envelope = JSON.parse(req.body);
  await onOcEvent(envelope);
  res.status(200).end();
});
§ oc.delegation · agent authority

Class A billable atom — sites pay once per delegation. Issue scope-limited authority to an agent. Revoke instantly. Production republishes a Nostr kind-30085 revocation envelope so any verifier drops subsequent envelopes signed under the delegation.

  • oc.delegation.issue(opts) (IssueDelegationOptions) => Promise<DelegationEnvelope>
    Max TTL 30 days. Class A billable.
  • oc.delegation.revoke(id) (string) => Promise<{ ok: true }>
    Free.
import { oc } from '@orangecheck/me-client';

const env = await oc.delegation.issue({
  agent_id: 'agent-pubkey-or-did',
  scope: ['inbox.read', 'attest.verify'],
  expires_at: new Date(Date.now() + 7 * 86400_000).toISOString(),
  reason: 'inbox triage agent',
});
// env.id, env.scope, env.expires_at, env.verify_url

// later — revoke. verifier rejects any envelope signed after this point.
await oc.delegation.revoke(env.id);
§ oc.event.fire · arbitrary billable subtypes

Fire any billable subtype your IntegratorPriceConfig enables — stamp_signing, attest_verification_at_gate, scoped_action_authorization, kyc_tier_upgrade, etc. session.create and payment.authorize are higher-level helpers; this is the escape hatch when you want one of the rarer subtypes. Class is determined by SUBTYPE_CLASS; cashback computed via computeFees(); recorded under your project_key.

  • oc.event.fire(opts) (FireEventOptions) => Promise<BillableEvent>
    Returns the canonical BillableEvent the server recorded.
import { oc } from '@orangecheck/me-client';

// fire any billable subtype your IntegratorPriceConfig enables.
const stamp = await oc.event.fire({
  project_key: 'pk_live_yourcompany',
  subtype: 'stamp_signing',
  action_label: 'review · march invoice',
});
// stamp.id, stamp.gross_fee_sats, stamp.user_earned_sats, stamp.verify_url

// for percent_of_amount-priced events, include the underlying amount:
const verify = await oc.event.fire({
  project_key: 'pk_live_yourcompany',
  subtype: 'attest_verification_at_gate',
  payment_amount_sats: 50_000,
});
§ types · TypeScript

Every type the SDK exports. Source of truth lives in oc-packages/me-client/src/types.ts and mirrors oc-me-web/src/lib/events/types.ts.

  • EventClass
  • EventSubtype
  • AttestTier
  • SiteFeeShape
  • IntegratorEventConfig
  • IntegratorPriceConfig
  • ComputedFees
  • ValidationResult
  • BillableEvent
  • Session
  • SessionPolicy
  • SignInOptions
  • PaymentAuthorizeOptions
  • PaymentResult
  • TelemetryEvent
  • OcAccount
  • OcSessionState
  • OcSessionStatus
  • OcAuthConfig
  • OcPublicJwk
  • VerifyResult
  • DelegationScope
  • DelegationEnvelope
  • IssueDelegationOptions
§ constants + helpers
  • PLATFORM_FEE_POLICY { pct: 0.2; min_floor_sats: 1; ratified: string }
  • MIN_INTEGRATOR_PRICE_SATS number = 5
  • computeFees(cfg, payment_amount_sats?) (IntegratorEventConfig, number?) => ComputedFees
  • validateIntegratorConfig(cfg) (IntegratorPriceConfig) => ValidationResult