Skip to main content
If you are unable to use Privy’s SDKs for signing, you can implement request signing directly in your service.
Implementing request signing directly is an advanced integration. Wherever possible, we suggest using Privy’s SDKs to handle request signing.

Steps

At a high-level, directly implementing request signing requires the following steps:
1

Build signature payload

Generate a JSON payload containing the following fields. All fields are required unless otherwise specified.
FieldTypeDescription
version1Authorization signature version. Currently, 1 is the only version.
method'POST' | 'PUT' | 'PATCH' | 'DELETE'HTTP method for the request. Signatures are not required on 'GET' requests.
urlstringThe full URL for the request. Should not include a trailing slash.
bodyJSONJSON body for the request.
headersJSONJSON object containing any Privy-specific headers, e.g. those that are prefixed with 'privy-'. This should not include any other headers, such as authentication headers, content-type, or trace headers.
headers['privy-app-id']stringPrivy app ID header (required).
headers['privy-idempotency-key']stringPrivy idempotency key header (optional). If the request does not contain an idempotency key, leave this field out of the payload.
2

Canonicalize signature payload

Next, canonicalize the payload per RFC 8785 and serialize it to a string. This GitHub repository links to various libraries for JSON canonicalization in different languages.
3

Sign signature payload

Sign the serialized JSON with ECDSA P-256 using the private key of your user key or authorization key and serialize it to a base64-encoded string.
4

Include the signature in request headers

Lastly, include the base64-encoded signature over the payload in the privy-authorization-signature header of your request to the Privy API.

Code examples

View code examples for signing requests in various languages below.
If the desired resource requires a user owner or user signer, make sure to request the user key before signing requests with it.
  • Vanilla TypeScript
  • Python
  • Rust
  • Go
import canonicalize from 'canonicalize'; // Support JSON canonicalization
import crypto from 'crypto'; // Support P-256 signing

// Replace this with your private key from the Dashboard
const PRIVY_AUTHORIZATION_KEY = 'wallet-auth:insert-your-private-key-here';
// ...

function getAuthorizationSignature({url, body}: {url: string; body: object}) {
  const payload = {
    version: 1,
    method: 'POST',
    url,
    body,
    headers: {
    'privy-app-id': 'insert-your-app-id'
    // If your request includes an idempotency key, include that header here as well
    }
  };

  // JSON-canonicalize the payload and convert it to a buffer
  const serializedPayload = canonicalize(payload) as string;
  const serializedPayloadBuffer = Buffer.from(serializedPayload);

  // Replace this with your user or authorization key. We remove the 'wallet-auth:' prefix
  // from authorization keys before using it to sign requests
  const privateKeyAsString = PRIVY_AUTHORIZATION_KEY.replace('wallet-auth:', '');

  // Convert your private key to PEM format, and instantiate a node crypto KeyObject for it
  const privateKeyAsPem = `-----BEGIN PRIVATE KEY-----\n${privateKeyAsString}\n-----END PRIVATE KEY-----`;
  const privateKey = crypto.createPrivateKey({
    key: privateKeyAsPem,
    format: 'pem'
  });

  // Sign the payload buffer with your private key and serialize the signature to a base64 string
  const signatureBuffer = crypto.sign('sha256', serializedPayloadBuffer, privateKey);
  const signature = signatureBuffer.toString('base64');
  return signature;
}

const authorizationSignature = getAuthorizationSignature({
  // Replace with your desired path
  url: 'https://api.privy.io/v1/wallets/<wallet_id>/rpc',
  // Replace with your desired body
  body: {
    method: 'personal_sign',
    params: {
    message: 'Hello world',
      // ...
    },
  }
});
I