For the complete documentation index, see llms.txt. This page is also available as Markdown.

Signature and verify

Signing and verification rules for all API and webhook interactions.

This document defines the signing and verification rules for all parties interacting with the UR ecosystem. It serves as the single source of truth for signature formats and authentication mechanisms.

Overview

The UR ecosystem uses two primary authentication methods depending on the API being accessed:

  • Partner Authentication (Server-to-Server): Used for UR-OPEN-API and Webhooks. Authenticates the Partner backend using a registered ECDSA key pair.

  • User Authentication (Wallet-to-Server): Used for UR-API. Authenticates individual end-users using their wallet's private key.

Both methods utilize Ethereum Personal Sign (EIP-191): "\x19Ethereum Signed Message:\n{len(messageToSign)}{messageToSign}"


Part A: Partner authentication (UR-OPEN-API & webhooks)

This method is used when a Partner backend calls UR APIs or when UR sends Webhooks to a Partner.

1. HTTP headers

Header
Description
Required

X-Api-Signature

Hexadecimal signature with 0x prefix.

Yes

X-Api-Deadline

The Unix timestamp (in seconds) indicating when the request expires. Setting this to 5 minutes from the current time.

Yes

X-Api-PublicKey

The Ethereum address of the signer.

Optional

2. Signing logic

Partner request (Partner -> UR)

  • Signer: Partner's registered backend Ethereum address.

  • Message Components:

    • requestBody: The exact raw JSON string in the request body.

    • deadline: The value sent in the X-Api-Deadline header, a timestamp within the next 5 minutes.

  • Message to Sign: Standard OpenAPI requests use messageToSign = "{requestBody} {deadline}" (note the single space between body and deadline). User-scoped FMA OpenAPI requests use messageToSign = "{canonicalPayload}urId:{X-Ur-Id}externalUserId:{X-External-User-Id} {deadline}"; see API reference: Managed Custody Mode.

  • Bodyless requests (GET / DELETE / HEAD): there is no request body, so the raw query string takes the place of requestBody. The message becomes messageToSign = "{rawQueryString} {deadline}" (an empty message, meaning no query string, is rejected).

  • Signing code example: Refers to this.

UR response / webhook (UR -> Partner)

  • Signer: UR Server address.

  • Message Components:

    • responseBody: The exact raw JSON string returned in the HTTP body.

  • Message to Sign: messageToSign = "{responseBody}"

3. Verification

The receiver must:

  • Read the raw body and relevant headers.

  • Construct the messageToSign as defined above.

  • Use EIP-191 recovery to extract the signer's Ethereum address from the X-Api-Signature.

  • Verify that the recovered address matches the expected/whitelisted address.


Part B: User authentication (UR-API)

This method is used for sensitive user operations (e.g., FX, Transfers) where a direct wallet signature is required.

1. HTTP headers

Header
Description

sign

The user's wallet signature.

hash

A Keccak256 hash of the business payload or a unique message.

deadline

The Unix timestamp (in seconds) indicating when the request expires. We recommend setting this to 20 minutes from the current time.

tokenId

The user's UR Token ID (URID).

2. Signing logic

  • Construct Base Message: baseMessage = hash + deadline (String concatenation).

  • Generate Intermediate Hash: intermediateHash = Keccak256(baseMessage).

  • Construct Final Message: finalMessage = "I agree to access my profile. " + intermediateHash.hex().

  • Sign: The user signs the finalMessage using their wallet (EIP-191).


Key pair

If you use the API sandbox, it generates and registers this key pair for you. The steps below are for generating your own key pair instead. See API signing key for both paths.

1. Generate key pair using Node.js (viem)

2. Key management recommendations

Private Key Storage: Use key management services like AWS Secrets Manager, HashiCorp Vault Public Key Registration: Synchronize the generated address (publicKey) with the UR system through secure channels


Code examples

signature

verify

User wallet signature


Important notes

  • Raw Body: Always use the raw HTTP body bytes. Do not re-serialize JSON, as key ordering or whitespace differences will cause signature mismatches.

  • Deadline Window: Recommended window is 1-5 minutes. Requests with an expired deadline are rejected. A deadline too far in the future is also rejected, but the maximum forward window is server-configured (typically 5 minutes) and is only enforced when that limit is set; it is not a hard-coded 5-minute constant.

  • Replay Protection: For high-concurrency environments, add a small random offset (1-5 seconds) to the deadline to ensure each request generates a unique signature.

  • Case Sensitivity: Ethereum addresses should be treated as case-insensitive but are typically stored/transmitted in lowercase or checksum format.

Environment addresses

Environment
UR Server Signature Address

Sepolia

0x4D2AA3f43De8f8BE746E315D291B804a4aBD3939

Mainnet

0xee28dEaD5F114C8405BE3be1144D59A4110B7F79

Last updated