{"templateId":"markdown","versions":[{"version":"v1.40","label":"v1.40 STS","link":"/products/custody/v1.40/concepts/authenticate-api-requests","default":true,"active":false,"folderId":"c15a2701"},{"version":"v1.39","label":"v1.39 STS","link":"/products/custody/v1.39/concepts/authenticate-api-requests","default":false,"active":false,"folderId":"c15a2701"},{"version":"v1.38","label":"v1.38 STS","link":"/products/custody/v1.38/concepts/authenticate-api-requests","default":false,"active":false,"folderId":"c15a2701"},{"version":"v1.34","label":"v1.34 LTS","link":"/products/custody/v1.34/concepts/authenticate-api-requests","default":false,"active":true,"folderId":"c15a2701"},{"version":"v1.26","label":"v1.26 LTS","link":"/products/custody/v1.26/concepts/authenticate-api-requests","default":false,"active":false,"folderId":"c15a2701"},{"version":"v1.19","label":"v1.19 LTS","link":"/products/custody/v1.19/concepts/authenticate-api-requests","default":false,"active":false,"folderId":"c15a2701"},{"version":"v1.15","label":"v1.15 LTS","link":"/products/custody/v1.15/concepts/authenticate-api-requests","default":false,"active":false,"folderId":"c15a2701"}],"sharedDataIds":{"sidebar":"sidebar-products/custody/@v1.15/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition","tabs","tab"]},"type":"markdown"},"seo":{"title":"Authenticate API requests","description":"User guides, API reference, and support resources.","siteUrl":"https://docs.ripple.com","lang":"en-US","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"authenticate-api-requests","__idx":0},"children":["Authenticate API requests"]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Executive summary"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Every API request requires a JWT token for session auth. State-changing operations additionally require a digital signature."]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["JWT tokens are obtained by signing a challenge with your private key."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tokens expire after 4 hours by default. Generate a new one when needed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Intents require an additional signature over the intent payload itself."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Each challenge can only be used once to prevent replay attacks."]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"success","name":"Why this matters"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Understanding this two-layer model is essential for API integrations. Read-only operations only need a valid JWT, but any action that modifies state (transfers, policy changes, user management) requires a cryptographic signature that creates a non-repudiable audit record. Your integration code must handle both layers correctly."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["For architects and operators"]},": Implement proper key management in your integration code—never hardcode private keys. Use environment variables, secrets managers, or HSM integrations. Plan for token refresh in long-running processes."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"prerequisites","__idx":1},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before authenticating API requests, you must complete:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/custody/v1.34/concepts/generate-keys-register"},"children":["Generate keys and register"]}," - Your key pair and user account must be set up"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You also need:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your private key (generated during registration)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your public key (in DER format with Base64 encoding)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The authorization server URL (contact your administrator)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your client ID (provided by your administrator)"]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"obtaining-a-jwt-token","__idx":2},"children":["Obtaining a JWT token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All API requests require a JWT (JSON Web Token) passed as a Bearer token in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization"]}," header. You need to obtain a JWT when you first start using the API and whenever your token expires."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Property"},"children":["Property"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Default expiration"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["4 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Token type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Bearer"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Issuer"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ripple OIDC server (default)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Auth server URL"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://openid.{environment_URL}/token"]}," (contact your administrator)"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"how-jwt-authentication-works","__idx":3},"children":["How JWT authentication works"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You generate a random challenge string."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You sign the challenge with your private key."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You submit the public key, challenge, and signature to the auth server."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The server verifies your signature and issues a JWT."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For security reasons, you can only use a challenge once. For every new authentication request, you must generate a new challenge and create a new signature."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-create-and-sign-a-challenge","__idx":4},"children":["Step 1: Create and sign a challenge"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create a random string (such as a UUID) and sign it with your private key."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"P-256 / secp256k1 (macOS)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Create a random challenge (use -n to prevent trailing newline)\necho -n \"$(uuidgen)\" > challenge.txt\n\n# Sign the challenge\nopenssl dgst -sha256 -sign privateKey.pem challenge.txt | base64\n","lang":"sh"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"P-256 / secp256k1 (Linux)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Create a random challenge (use -n to prevent trailing newline)\necho -n \"$(uuidgen)\" > challenge.txt\n\n# Sign the challenge\nopenssl dgst -sha256 -sign privateKey.pem challenge.txt | base64 -w0\n","lang":"sh"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Ed25519 (macOS)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Create a random challenge (use -n to prevent trailing newline)\necho -n \"$(uuidgen)\" > challenge.txt\n\n# Sign the challenge\nopenssl pkeyutl -sign -inkey privateKey.pem -rawin -in challenge.txt -out sig.dat\n\n# Convert to DER format and Base64 encode\ncat sig.dat | hexdump -v -e '/1 \"%02x\"' | sed 's/\\(.\\{64\\}\\)\\(.\\{64\\}\\)/30440220\\10220\\2/g' | xxd -r -p | base64 -b0\n","lang":"sh"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Ed25519 (Linux)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Create a random challenge (use -n to prevent trailing newline)\necho -n \"$(uuidgen)\" > challenge.txt\n\n# Sign the challenge\nopenssl pkeyutl -sign -inkey privateKey.pem -rawin -in challenge.txt -out sig.dat\n\n# Convert to DER format and Base64 encode\ncat sig.dat | hexdump -v -e '/1 \"%02x\"' | sed 's/\\(.\\{64\\}\\)\\(.\\{64\\}\\)/30440220\\10220\\2/g' | xxd -r -p | base64 -w0\n","lang":"sh"},"children":[]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-request-a-token","__idx":5},"children":["Step 2: Request a token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Submit your credentials to the authorization server:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"POST https://openid.{environment_URL}/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=password\n&client_id=YOUR_CLIENT_ID\n&signature=<base64_encoded_signature>\n&challenge=<challenge_string>\n&public_key=<base64_encoded_public_key>\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-3-receive-and-store-the-token","__idx":6},"children":["Step 3: Receive and store the token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The server returns a JWT:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"access_token\": \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 14400\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expires_in"]}," value is in seconds (14400 = 4 hours)."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-4-use-the-token","__idx":7},"children":["Step 4: Use the token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Include the JWT in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization"]}," header of all API requests:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"GET /v1/domains\nAuthorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"signing-intents-state-changing-operations","__idx":8},"children":["Signing intents (state-changing operations)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All state-changing operations require a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["digital signature"]}," in addition to the JWT. These operations are called \"intents\" and include:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Creating entities (users, accounts, domains)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Updating entities"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Approving or rejecting intents"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Creating transactions"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The signature both proves that you explicitly authorized the specific action, and creates an immutable audit trail."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"how-to-sign-an-intent","__idx":9},"children":["How to sign an intent"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Prepare the request object"]},": Build the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request"]}," object containing author, expiry, target domain, and payload (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#intent-request-structure"},"children":["Intent request structure"]}," below)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Canonicalize"]},": Convert the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request"]}," object to canonical JSON format (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#canonicalization-rules"},"children":["Canonicalization rules"]}," below)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Sign"]},": Hash the canonical JSON with SHA-256 and sign with your private key"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Encode the signature"]},": Serialize the signature in DER format with Base64 encoding"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Add to request body"]},": Include the signature in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}," field of the request body (not a header)"]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"intent-request-structure","__idx":10},"children":["Intent request structure"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All intents follow this structure. The signature is computed over the canonicalized ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request"]}," object only:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"request\": {\n    \"author\": {\n      \"id\": \"<your-user-uuid>\",\n      \"domainId\": \"<your-domain-uuid>\"\n    },\n    \"expiryAt\": \"2026-02-25T12:00:00.000Z\",\n    \"targetDomainId\": \"<target-domain-uuid>\",\n    \"id\": \"<new-intent-uuid>\",\n    \"customProperties\": {},\n    \"payload\": {\n      \"type\": \"v0_CreatePolicy\",\n      \"...\": \"payload-specific fields\"\n    },\n    \"description\": \"Human-readable description\",\n    \"type\": \"Propose\"\n  },\n  \"signature\": \"<base64-signature-of-canonicalized-request-object>\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Required fields"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customProperties"]}," field is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["mandatory"]}," at the request level. Use an empty object ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{}"]}," if you have no custom properties. Missing this field causes a validation error."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"canonicalization-rules","__idx":11},"children":["Canonicalization rules"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Canonical JSON ensures that identical data always produces identical signatures, regardless of how the JSON was originally formatted. The rules are:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Rule"},"children":["Rule"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Example"},"children":["Example"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Remove whitespace"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No spaces, tabs, or newlines"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"a\":\"b\"}"]}," not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{ \"a\": \"b\" }"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Sort keys alphabetically"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["All object keys sorted A-Z, recursively through nested objects"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"a\":1,\"b\":2}"]}," not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"b\":2,\"a\":1}"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Omit null values"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Do not include fields with null values in your JSON"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"a\":1}"]}," not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"a\":1,\"b\":null}"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Preserve array order"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Arrays of objects keep their original order—do not sort array elements"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[{\"z\":1},{\"a\":2}]"]}," stays as-is"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["No trailing newline"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The canonicalized output must not end with a newline character"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jq -j"]}," flag"]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Null values cause signature failures"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Do ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," include fields with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," values in your JSON. The server removes null values during canonicalization, so if your JSON contains ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"field\": null"]},", your canonicalized version will differ from the server's, causing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InvalidSignatureError"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Wrong"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"intentTypes\": null, \"customProperties\": {}}"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Correct"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"customProperties\": {}}"]}," (omit the null field entirely)"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Example transformation:"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Original JSON:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"v0_CreateTransactionOrder\",\n  \"note\": null,\n  \"details\": {\n    \"amount\": \"100\",\n    \"asset\": \"USD\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Canonical JSON:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"details\":{\"amount\":\"100\",\"asset\":\"USD\"},\"type\":\"v0_CreateTransactionOrder\"}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"example-sign-an-intent-payload","__idx":12},"children":["Example: Sign an intent payload"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jq"]}," to canonicalize the JSON before signing:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"cat intent.json | jq -j -S -c > intent_sorted.json\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["jq flags explained:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-S"]}," — Sort object keys alphabetically"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-c"]}," — Compact output (removes whitespace)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-j"]}," — No trailing newline (critical—a newline changes the hash)"]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-j"]}," flag is essential. Without it, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jq"]}," adds a trailing newline that becomes part of the hashed content, causing signature verification to fail."]}]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"P-256 / secp256k1 (macOS)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Canonicalize the JSON payload\ncat intent.json | jq -j -S -c > intent_sorted.json\n\n# Sign the sorted payload\nopenssl dgst -sha256 -sign privateKey.pem intent_sorted.json | base64 -b0\n","lang":"sh"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"P-256 / secp256k1 (Linux)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Canonicalize the JSON payload\ncat intent.json | jq -j -S -c > intent_sorted.json\n\n# Sign the sorted payload\nopenssl dgst -sha256 -sign privateKey.pem intent_sorted.json | base64 -w0\n","lang":"sh"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Ed25519 (Linux)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Canonicalize the JSON payload\ncat intent.json | jq -j -S -c > intent_sorted.json\n\n# Hash and sign\ntmp=$(mktemp)\ncat intent_sorted.json | sha256sum | xxd -r -p > ${tmp}\nopenssl pkeyutl -sign -inkey privateKey.pem -rawin -in ${tmp} | hexdump -v -e '/1 \"%02x\"' | sed 's/\\(.\\{64\\}\\)\\(.\\{64\\}\\)/30440220\\10220\\2/g' | xxd -r -p | base64 -w0\nrm -f ${tmp}\n","lang":"sh"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Ed25519 (macOS)","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Canonicalize the JSON payload\ncat intent.json | jq -j -S -c > intent_sorted.json\n\n# Hash and sign\ntmp=$(mktemp)\ncat intent_sorted.json | sha256sum | xxd -r -p > ${tmp}\nopenssl pkeyutl -sign -inkey privateKey.pem -rawin -in ${tmp} | hexdump -v -e '/1 \"%02x\"' | sed 's/\\(.\\{64\\}\\)\\(.\\{64\\}\\)/30440220\\10220\\2/g' | xxd -r -p | base64 -b0\nrm -f ${tmp}\n","lang":"sh"},"children":[]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"complete-example-create-a-policy","__idx":13},"children":["Complete example: Create a policy"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This example shows the full flow for submitting a signed intent to create a policy."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["1. Create the request object"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request.json"]},"):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"author\": {\n    \"id\": \"461dac3d-55c6-4ce8-9ce2-dfb97cfbe279\",\n    \"domainId\": \"67241e7d-f345-459e-9293-475693c45d85\"\n  },\n  \"expiryAt\": \"2026-02-25T12:00:00.000Z\",\n  \"targetDomainId\": \"67241e7d-f345-459e-9293-475693c45d85\",\n  \"id\": \"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa\",\n  \"customProperties\": {},\n  \"payload\": {\n    \"id\": \"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb\",\n    \"alias\": \"Daily Operations Policy\",\n    \"rank\": 100,\n    \"intentTypes\": [\"v0_CreateTransferOrder\"],\n    \"scope\": \"Self\",\n    \"scriptingEngine\": \"Javascript_v0\",\n    \"condition\": {\n      \"expression\": \"true\",\n      \"type\": \"Expression\"\n    },\n    \"workflow\": [{\"role\": \"admin\", \"quorum\": 1, \"type\": \"RoleQuorum\"}],\n    \"lock\": \"Unlocked\",\n    \"customProperties\": {},\n    \"type\": \"v0_CreatePolicy\"\n  },\n  \"description\": \"Create a policy for daily operations\",\n  \"type\": \"Propose\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["2. Canonicalize and sign"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Canonicalize (sort keys, compact, no trailing newline)\ncat request.json | jq -j -S -c > request_canonical.json\n\n# Sign (P-256/secp256k1 on macOS)\nSIGNATURE=$(openssl dgst -sha256 -sign privateKey.pem request_canonical.json | base64 | tr -d '\\n')\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["3. Build and submit the full request"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"# Build the full request body with signature\nREQUEST=$(cat request.json)\ncat > intent_body.json << EOF\n{\n  \"request\": $REQUEST,\n  \"signature\": \"$SIGNATURE\"\n}\nEOF\n\n# Submit to the API\ncurl -X POST \"https://api.example.com/v1/intents\" \\\n  -H \"Authorization: Bearer $JWT_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @intent_body.json\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Expected response"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"requestId\": \"8737c2a2-e8e1-46bf-a2a4-9918437f4a29\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The signature is computed from the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["canonicalized"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request"]}," object. The server canonicalizes your request and verifies the signature matches. Any difference in canonicalization (including null values or trailing newlines) will cause signature verification to fail."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"approving-intents","__idx":14},"children":["Approving intents"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Approving an intent uses a different request structure than proposing. For complete details on the approval workflow, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/custody/v1.34/api/get-started/key-operations/update/intent-approval"},"children":["Approve and reject intents"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"approval-request-structure","__idx":15},"children":["Approval request structure"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Proposal"},"children":["Proposal"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Approval"},"children":["Approval"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required (new intent UUID)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not used"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["intentId"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not used"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required (existing intent UUID)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payload"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required (the operation details)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not used"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["proposalSignature"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not used"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required (from original proposal)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expiryAt"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"Propose\""]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"Approve\""]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Example approval request:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"request\": {\n    \"author\": {\n      \"id\": \"828b554c-c9c9-11eb-a79b-dcfb48cfb3cb\",\n      \"domainId\": \"455ad43e-cdd9-11eb-8465-dcfb48cfb3cb\"\n    },\n    \"targetDomainId\": \"455ad43e-cdd9-11eb-8465-dcfb48cfb3cb\",\n    \"expiryAt\": \"2026-02-26T12:00:00.000Z\",\n    \"intentId\": \"846b5c3c-27e3-4146-ab18-ce6732624d35\",\n    \"proposalSignature\": \"<signature-from-original-proposal>\",\n    \"approvalReason\": \"Approved after review\",\n    \"type\": \"Approve\"\n  },\n  \"signature\": \"<base64-signature-of-canonicalized-request-object>\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["proposalSignature"]}," is retrieved from the original intent's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data.details.proposalSignature"]}," field."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"jwt-and-intent-author-identity","__idx":16},"children":["JWT and intent author identity"]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Critical: JWT must match intent author"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The JWT token identifies the API caller. When submitting an intent (proposal or approval), the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["author.id"]}," in the request ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["must correspond to the user identified by the JWT"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You cannot use one user's JWT to submit intents on behalf of another user. This is enforced at the API layer."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"why-this-matters","__idx":17},"children":["Why this matters"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The two-layer authentication model enforces:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["JWT identifies the caller"]}," — The server extracts user identity from the JWT token"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Signature proves authorization"]}," — The digital signature proves the author explicitly authorized the action"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Both must be consistent"]}," — The JWT user and the intent author must be the same person"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This prevents delegation attacks where a compromised JWT could be used to forge actions for other users."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":18},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Error"},"children":["Error"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Resolution"},"children":["Resolution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InvalidJwtError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["JWT token is expired or malformed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Obtain a new JWT token"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InvalidSignatureError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Signature doesn't match the payload"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify payload is properly canonicalized (sorted, no whitespace, no nulls)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InvalidSignatureError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Wrong private key used"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure you're signing with the private key that matches your registered public key"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InvalidSignatureError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Trailing newline in challenge"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["echo -n"]}," when creating the challenge to prevent trailing newlines"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PermissionDeniedError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["JWT user doesn't match intent author"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure the JWT token belongs to the user specified in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["author.id"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PermissionDeniedError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User lacks permission for the operation"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Check your user roles and domain permissions with your administrator"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PermissionDeniedError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User not in domain's readAccess"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure your roles are included in the domain's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["readAccess"]}," permissions"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InvalidIntentError: This user has already signed"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Proposer attempting to approve own intent"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A different user must approve; proposers cannot self-approve (dual control)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["401 Unauthorized"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Missing or invalid Authorization header"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure the JWT is included as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Bearer <token>"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-topics","__idx":19},"children":["Related topics"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/custody/v1.34/concepts/authentication"},"children":["Authentication"]}," - Conceptual overview of authentication in ","Ripple Custody"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/custody/v1.34/concepts/generate-keys-register"},"children":["Generate keys and register"]}," - One-time setup for new users"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/custody/v1.34/api/get-started/key-operations/update/intent-approval"},"children":["Approve and reject intents"]}," - Complete approval workflow"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/custody/v1.34/concepts/data-integrity#verifying-custody-signatures"},"children":["Verifying Custody signatures"]}," - How to verify signatures on data returned by the platform"]}]}]},"headings":[{"value":"Authenticate API requests","id":"authenticate-api-requests","depth":1},{"value":"Prerequisites","id":"prerequisites","depth":2},{"value":"Obtaining a JWT token","id":"obtaining-a-jwt-token","depth":2},{"value":"How JWT authentication works","id":"how-jwt-authentication-works","depth":3},{"value":"Step 1: Create and sign a challenge","id":"step-1-create-and-sign-a-challenge","depth":3},{"value":"Step 2: Request a token","id":"step-2-request-a-token","depth":3},{"value":"Step 3: Receive and store the token","id":"step-3-receive-and-store-the-token","depth":3},{"value":"Step 4: Use the token","id":"step-4-use-the-token","depth":3},{"value":"Signing intents (state-changing operations)","id":"signing-intents-state-changing-operations","depth":2},{"value":"How to sign an intent","id":"how-to-sign-an-intent","depth":3},{"value":"Intent request structure","id":"intent-request-structure","depth":3},{"value":"Canonicalization rules","id":"canonicalization-rules","depth":3},{"value":"Example: Sign an intent payload","id":"example-sign-an-intent-payload","depth":3},{"value":"Complete example: Create a policy","id":"complete-example-create-a-policy","depth":3},{"value":"Approving intents","id":"approving-intents","depth":2},{"value":"Approval request structure","id":"approval-request-structure","depth":3},{"value":"JWT and intent author identity","id":"jwt-and-intent-author-identity","depth":2},{"value":"Why this matters","id":"why-this-matters","depth":3},{"value":"Troubleshooting","id":"troubleshooting","depth":2},{"value":"Related topics","id":"related-topics","depth":2}],"frontmatter":{"seo":{"title":"Authenticate API requests"}},"lastModified":"2026-06-23T20:41:27.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/custody/v1.34/concepts/authenticate-api-requests","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}