Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Headless Mode

Headless mode submits a 1auth intent with signatures produced outside the passkey dialog. Use it when the ERC-7579 account has another installed validator and your app can produce its expected signatures.

1auth prepares the intent and forwards opaque signature bytes; the installed on-chain validator decides whether they are valid.

For example, the non-passkey signer might be:

  • an app-managed ECDSA signer,
  • a backend or AWS KMS signing service,
  • another ERC-7579 validator your app has installed out of band.

Use OneAuthClient.sendIntent when every intent should show a passkey approval dialog.

What 1auth does

Headless mode still provides:

  • /api/intent/prepare?signingMode=headless quotes the intent, performs balance checks, and returns the message hashes your validator signer must sign.
  • Sponsorship helpers mint access and extension JWTs for apps that sponsor intents.
  • /api/intent/headless-execute forwards your pre-encoded validator signatures to the orchestrator.
  • Status and wait endpoints work the same way as the passkey flow.

1auth treats the submitted signatures as opaque bytes. It does not open clear signing UI and does not verify the validator signature locally.

Requirements

Before using headless mode:

  • the account must already have a validator installed that can authorize the intent,
  • your app must know how to sign the prepared hashes for that validator,
  • your app must encode the signatures in the validator-prefixed format expected by the orchestrator,
  • submitted calls must stay inside whatever policy or authorization the installed validator enforces.

This guide covers only the prepare-sign-submit path. For SmartSession validators, use the Permissions flow for setup, policy review, and session-key handling.

Prepare

import { OneAuthHeadlessClient } from "@rhinestone/1auth/headless";
 
const headless = new OneAuthHeadlessClient({
  providerUrl: "https://passkey.1auth.app",
  sponsorship: {
    accessTokenUrl: "/api/sponsorship/access-token",
    extensionTokenUrl: "/api/sponsorship/extension-token",
  },
});
 
const prepared = await headless.prepareIntent({
  accountAddress,
  targetChain: 84532,
  calls: [
    {
      to: vault,
      data: depositCalldata,
      value: 0n,
    },
  ],
});

prepareIntent() returns intentOp and digestResult for unchanged submission, per-origin hashes, destination and target-execution hashes when required, and the echoed account, calls, target chain, and expiry.

Sign

Sign the prepared hashes with the non-passkey validator your app controls. 1auth does not prescribe the signer or encoding here; use the encoder for your validator.

const {
  originSignatures,
  destinationSignature,
  targetExecutionSignature,
} = await signWithInstalledValidator({
  prepared,
  validatorContext,
});

The contents of validatorContext depend on the installed validator.

Submit

const result = await headless.submitIntent({
  intentOp: prepared.intentOp,
  digestResult: prepared.digestResult,
  binding: prepared.binding,
  accountAddress: prepared.accountAddress,
  targetChain: prepared.targetChain,
  calls: prepared.calls,
  expiresAt: prepared.expiresAt,
  originSignatures,
  destinationSignature,
  targetExecutionSignature,
});

prepareIntent() fixes the funding mode before signing and returns the quote-time sponsorshipToken for sponsored submission. Use sponsorshipMode: "preferred" to allow a self-funded re-quote, or "disabled" to request self-funding.

Wait

const final = await headless.waitForIntent(
  result.intentId,
  result.transactionResult,
);

waitForIntent() returns the latest orchestrator status and any available transaction hash.

Trust Model

Headless mode removes the per-intent passkey dialog, so your app decides what to sign. 1auth binds a sponsored intent's extension token to the prepared intentOp, but a malicious or buggy app-side signer can still sign the wrong thing.

The safety boundary is the installed validator:

  • if the validator approves the submitted signature, the intent can execute,
  • if the calls drift outside that validator's policy, the validator should reject on-chain.