Agnic
Partner Program

Partner-Branded Delegation

Let your users authorize AI agent spending with a passkey-signed mandate — one deep link, on a page wearing your brand.

Before an AI agent can spend on a user's behalf, the user has to say how much, where, and until when. That authorization is a delegation (an IntentMandate): a spending scope — categories, per-transaction and daily limits, optional merchant whitelist — signed on the user's own device with a passkey and issued as a verifiable credential.

Partner-Branded Delegation is the hosted page where that happens: your app opens one deep link, the user reviews the scope on a page wearing your wordmark and accent color, confirms with Touch ID / their device passkey, and your app gets signaled the moment the mandate exists.

  • Wears your wordmark and accent color (same dashboard settings as Checkout)
  • Opens as a desktop popup or mobile full-page redirect
  • You can suggest the scope via query params — the user always reviews, can edit anything, and signs exactly what's on screen

Prefill is a suggestion, never an instruction. Whatever limits you pass in the URL, the user sees them, can change them, and the passkey signs the final on-screen scope. Server-side validation is authoritative. You cannot create or widen a mandate on a user's behalf.


The flow

┌────────────────┐                                          ┌────────────────┐
│                │  1. user clicks "Authorize agent"        │                │
│  Your app      │─────────────────────────────────────────▶│  popup /       │
│                │                                          │  redirect      │
└────────┬───────┘                                          └────────┬───────┘
         │                                                           │
         │                                2. Delegation form loads
         │                                (your brand, your suggested limits)
         │                                                           │
         │                                3. user reviews, edits, signs
         │                                   with device passkey
         │                                                           │
         │     4. 'agnic:delegation_created' postMessage / redirect  │
         │◀──────────────────────────────────────────────────────────┘


  agents can now spend within the signed scope
PartyOwns
YouOpen the deep link (optionally with suggested limits); listen for completion
AgnicRenders the form; passkey ceremony; issues + stores the mandate

Step 1 — Brand the page

Same dashboard fields as Checkout and Add Card — Display name and Accent color under your OAuth client's Checkout settings at app.agnic.ai.


URL format

https://app.agnic.ai/partner/delegations/new
  ?client_id={YOUR_CLIENT_ID}
  &return_url={URL_ENCODED_RETURN}
  &categories=food_beverage,merchandise      # optional prefill
  &max_per_tx=50.00                          # optional prefill
  &daily_limit=200.00                        # optional prefill
  &currency=USD                              # optional prefill
  &valid_days=30                             # optional prefill
  &merchant_whitelist=merchant_maple_grounds # optional prefill
ParamRequiredNotes
client_idYour app_… client id. Must be approved and not revoked.
return_urlAbsolute URL, URL-encoded. Must match one of the client's registered redirect_uris.
categoriesComma-separated: payment, general, food_beverage, alcohol, transportation, merchandise, subscription
max_per_txDecimal string, e.g. 50.00
daily_limitDecimal string; must be ≥ max_per_tx to pass form validation
currencyUSD, CAD, GBP, or EUR
valid_daysInteger 1–365
merchant_whitelistComma-separated Agnic merchant IDs

Malformed prefill values are silently dropped to sensible defaults — a bad deep link never blocks the user.

Reference implementation

const handleAuthorizeAgent = () => {
  const returnUrl = `${window.location.origin}/your-page`;
  const clientId = process.env.NEXT_PUBLIC_AGNIC_OAUTH_CLIENT_ID!;
  const params = new URLSearchParams({
    client_id: clientId,
    return_url: returnUrl,
    categories: "food_beverage,merchandise",
    max_per_tx: "50.00",
    daily_limit: "200.00",
    currency: "USD",
  });
  const url = `https://app.agnic.ai/partner/delegations/new?${params}`;

  if (window.innerWidth < 640) {
    window.location.href = url;
    return;
  }
  const width = 520;  // the scope form is taller than Checkout — give it room
  const height = 860;
  const left = Math.max(0, Math.round(window.screenX + (window.outerWidth - width) / 2));
  const top = Math.max(0, Math.round(window.screenY + (window.outerHeight - height) / 2));
  window.open(url, "agnic-delegation", `width=${width},height=${height},left=${left},top=${top},popup=yes`);
};

The popup is a top-level window, so the passkey prompt (Touch ID / Windows Hello / phone) works exactly as it does on any normal page.


Step 3 — Handle the return

Desktop: postMessage

useEffect(() => {
  const onMessage = (ev: MessageEvent) => {
    if (ev.origin !== "https://app.agnic.ai") return;
    if (!ev.data || typeof ev.data !== "object") return;

    if (ev.data.type === "agnic:delegation_created") {
      toast.success("Agent spending authorized.");
      refreshDelegationStatus();
    }
    if (ev.data.type === "agnic:delegation_cancelled") {
      // user closed without signing — no-op
    }
  };
  window.addEventListener("message", onMessage);
  return () => window.removeEventListener("message", onMessage);
}, []);
typePayloadWhen
agnic:delegation_createdMandate signed and issued
agnic:delegation_cancelledUser closed the popup before signing

The message is a success signal only — the credential itself is never exposed to your app.

Mobile: ?delegation=success on the return URL

ParamValuesNotes
delegationsuccess | cancelledAppended to your return_url on mobile

Handle it the same way as the Checkout mobile return, with delegation in place of topup.


Security model — what this means for you

ConcernHow Delegation handles it
Partner inflating limitsPrefill is display-only. The passkey signs the scope the user sees; the server validates limits.
Arbitrary redirect (open-redirect abuse)return_url must match a redirect_uri on your OAuth client. Rejected otherwise.
postMessage spoofing from another tabYou validate ev.origin === "https://app.agnic.ai" in your listener.
Credential leakageThe signed mandate (SD-JWT) never leaves Agnic — your app only gets a success signal.
Who the mandate binds toThe user's own passkey, on their own device, on their own Agnic account. Strictly user-scoped.

Testing

Manual smoke-test checklist

  • Desktop Chrome: popup opens 520×860, form shows your prefilled limits, all fields editable
  • Sign with passkey → success screen → agnic:delegation_created fires → popup closes ~2 s later
  • First-time user (no passkey yet): registration prompt then signing prompt — two ceremonies is expected
  • Malformed prefill (max_per_tx=abc, unknown category) → form falls back to defaults, no error
  • Click ✕ → agnic:delegation_cancelled fires
  • Mobile (viewport < 640 px): full-page redirect; success returns return_url?delegation=success
  • return_url not registered → error page, no form shown

FAQ

What if the user already has a delegation? The form shows an "active mandate" notice; signing a new one supersedes the old. There is one active mandate per user, not per partner.

Can I read the mandate's limits afterwards? No. The mandate belongs to the user. Your app learns only that one exists (success signal). Agents you run through Agnic's APIs will simply succeed or hit approval prompts based on the user's actual scope.

Why a passkey and not just a confirmation button? The authenticator signs the exact scope shown — a cryptographic record of what the user authorized, usable in disputes. A button click proves nothing later.

Can this run in an iframe? No — same policy as Add Card: the page refuses framing, and passkey ceremonies are unreliable in cross-origin iframes anyway. Use the popup.