openapi: 3.0.0
info:
version: 2.0.0
title: Anchorage Digital API Reference Addresses API
contact:
email: api@anchorage.com
description: "# Introduction\n*CONFIDENTIAL: Please do not distribute this documentation externally without prior Anchorage Digital approval.*\n\n\nThe Anchorage Digital REST API v2.0 provides a set of operations and resources that allow Anchorage Digital clients and partners to:\n - Programmatically transfer funds from an Anchorage Digital vault or wallet without human intervention\n - Create and list deposit addresses in a vault\n - Read and monitor vault balances\n - Query transaction history including deposits\n - Request quotes from and execute trades with the Anchorage Digital trading desk\n\n\nWant help or to share your opinion on how this API works for you? Please contact api@anchorage.com.\n\n\n# Authentication and Security\n\nThe Anchorage Digital API performs authentication and authorization via a combination of:\n\n\n* An API key, which is a bearer token\n\n* A permission group signed by the user's organization, which is linked to the API key\n\n* An Ed25519 Signature, which comes from a user-generated key and is required for certain requests\n\n\n## Permission Groups\n\n\nA permission group acts as a set of rules for how an organization and its resources can be accessed. Permission groups are created independently of API keys, and new permission groups must be created prior to making an API key.\n\n\nEach permission group has a name, a description and a set of permissions which can be applied to your organization's vaults. Updating, creating and deleting permissions groups require a quorum of approvals. After creation, a permission group can be freely assigned to an unlimited number of API keys.\n\n\nEach API key inherits its permissions from the associated permission group. When the permission group is updated, all API keys associated with it will inherit the updated permission set. If a permission group is deleted, all associated API keys will no longer work.\n\n\nEach organization is created with a default permission group that allows read-only access. This permission group may be modified or deleted at any time, and no API keys are created by default with this permission group.\n\n\n### Permissions\n\n\nPossible vault permissions include the following:\n\n\n
\n \n | Read vault activity (READ) | \n See an overview of your vault(s) and wallets. Read vault details, balances, asset types, transaction history and deposit addresses. | \n
\n \n | Create address (CREATE_DEPOSIT_ADDRESS) | \n Receive deposits in the vault from external sources. Create and read deposit addresses. | \n
\n \n | Transfer funds (TRANSFER) | \n This permission is configurable to enable an API key endowed with this permission to either 1) Transfer funds to any Anchorage Digital institutional account, including those outside of your organization or 2) Transfer funds to any blockchain address not custodied by Anchorage Digital that has gone through quorum approval. | \n
\n \n | Propose and accept settlements (PROPOSE_ACCEPT_SETTLEMENTS) | \n This is an Atlas specific permission for initiating settlements. | \n
\n \n | Authorize settlements (AUTHORIZE_SETTLEMENTS) | \n This is an Atlas specific permission for authorizing settlements after they've been proposed or accepted. | \n
\n
\n\n\nThere are also special vault permissions for enabled by Anchorage Digital on a per-organization basis:\n\nAdditionally, there are global permissions which apply to the entire organization:\n\n\n\n \n | Initiate withdrawals (INITIATE_WITHDRAWAL) | \n Initiate withdrawals to external destinations. All withdrawals require quorum approval through the Anchorage Digital mobile app. | \n
\n \n | Execute trades (TRADE) | \n Request for quotes (RFQ) from the Anchorage Digital trading desk. Create and accept quotes. Read data of trades and settlements created by this key. | \n
\n \n | Read trade activity (READ_TRADE) | \n Read trade activity and trade settlements data of your organization. | \n
\n \n | Read lending activity (LENDER_READ) | \n Read lending activity of your organization. | \n
\n \n | Read facility data (FACILITY_ONLY) | \n Read lending facility data. | \n
\n \n | Read deposit attribution activity (READ_DEPOSIT_ATTRIBUTION) | \n Read deposit attribution activity of your organization. | \n
\n \n | Manage deposit attributions (DEPOSIT_ATTRIBUTION) | \n Read deposit attribution activity of your organization. Perform deposit attributions. | \n
\n \n | Initiate Staking and Unstaking (STAKE) | \n Initiate staking or unstaking operation. All operations require quorum approval through the Anchorage Digital mobile app. | \n
\n
\n\n\n### Example Workflow - Allow transfers from specific vaults\n\nTo create an API key with the ability to transfer funds from an Anchorage Digital vaults or wallet, a permission group must first be created with the **Transfer funds** permission for the desired source vaults. Creating this permission group requires a quorum of approvals on the Anchorage Digital iOS app. Once the creation is confirmed, any number of API keys may be created with this permission group in the Anchorage Digital Web Dashboard under the API 2.0 section.\n\nTo add additional permissions to the API key, update the associated permission group through the Anchorage Digital Web Dashboard. To revoke any permissions, the individual API key may be revoked, or the permission group may be updated or deleted to remove Transfer access.\n\n## API Keys\n\nAll API requests must be made over HTTPS and must include authentication using the following scheme.\n\n\n\n### Generating an API Key\n\nIn order to make a valid API request, you must first create an API key. API keys can be created and managed in the Anchorage Digital Web Dashboard under the [API 2.0 tab](https://anchoragelogin.com/api). When you create an API key, there are 3 pieces of information you will need to remember:\n - API access key\n - Ed25519 public key (optional for read-only requests)\n - Ed25519 private/signing key (optional for read-only requests)\n\nYou must generate an Ed25519 signing key pair and save the public portion in the Anchorage Digital Web Dashboard when creating the API access key. The signing key pair is used for added security with sensitive requests.\n\nPlease note, Anchorage Digital cannot recover your API access key or private signing key if you forget them. You may generate a new access key and signing key at any time if you lose access.\n\n### API Key Permissions\n\nEach API key is associated with a permission group. This permission group specifies the permitted actions for all associated API keys. Read more about permission groups [here](#section/Authentication-and-Security/Permission-Groups).\n\n### Creating a request\n\nAll requests must include the `Api-Access-Key` header, which contains your API access key as a string.\n\nEndpoints that require a signature must include the `Api-Signature` and `Api-Timestamp` headers. Read more about signatures [here](#section/Authentication-and-Security/Signatures).\n\nAll request bodies must be valid JSON and have the content type `application/json`.\n\n## Request Signatures\n\nCertain endpoints require an Ed25519 signature to be provided alongside the API key. These endpoints will specify the `Api-Signature` and `Api-Timestamp` headers as additional parameters.\n\nSignatures are optional unless explicitly required, but are encouraged for all requests. If a signature is provided, it will be verified.\n\n### Signing Keys\n\nA signing key pair is generated by the user and the corresponding public key must be provided when creating an API key.\n\nWhen creating an API key, you will be prompted to provide an Ed25519 public key. You must use the associated Ed25519 signing key (private key) when creating signatures for requests from this API key.\n\nPlease note that signing keys (Ed25519 private keys) should be stored securely by the user. The signing key should only be used to derive request signatures and should never be sent in a request. Anchorage Digital will never request you share your private key.\n\n### Generating a Signing Key\n\nThe user must securely generate an Ed25519 key pair on their own hardware and retain both the public and private portions. The Anchorage Digital API accepts a 64-character (32 bytes) hex-encoded Ed25519 public key when creating an API access key.\n\n\n#### Code sample (Python)\n\n*Generate a new signing key pair*\n\n```python\n# https://pypi.org/project/PyNaCl/\n\nimport nacl\nimport nacl.signing\nimport secrets\n\nseed = secrets.token_bytes(32)\n\n# Generate a new random signing key\nsigning_key = nacl.signing.SigningKey(seed)\n\n# Obtain the hex-encoded signing key\nprint('Signing key:')\nprint(signing_key.encode().hex())\n\n# Obtain the hex-encoded verify key for the given signing key\n# Use this in the Anchorage Digital Web Dashboard when creating an API key\nprint('Public key:')\nprint(signing_key.verify_key.encode().hex())\n```\n\n\n### Signing a Request\n\nTo sign a request, generate a request signature using the Ed25519 private (signing) key and provide it alongside the request in the `Api-Signature` header.\n\nTo create a request signature, first concatenate the `timestamp`, `method`, `request path`, and `body` into a string. Then, create a signature of this message using the Ed25519 private key and hex-encode the output. Use this value as the `Api-Signature` header and use the `timestamp` value as the `Api-Timestamp` header.\n\n- The `method` is an uppercase HTTP method (ex. `GET`, `POST`, `DELETE`)\n- The `request path` should contain all query parameters (ex. `/v2/transfers?foo=bar&baz=bang`)\n- The `body` is a stringified HTTP request body\n- The `body` should be omitted if the request does not contain a body (ex. a `GET` or `DELETE` request)\n- The `timestamp` is the same as the `Api-Timestamp` header\n- The `timestamp` is a number of seconds since the Unix Epoch in UTC, and must be within one minute of the API service's time when the request is received\n\n### Reference signature\n\nTo verify your signature generation code is correct, generate a signature for the following request and timestamp using the provided signing key. If the generated signature matches the signature below, your signature generation code is correct.\n\n\n\n \n | Timestamp | \n 1577880000 | \n
\n \n | HTTP Method (Uppercase) | \n POST | \n
\n \n | HTTP Path + query | \n /v2/transfers?foo=bar&baz=bang | \n
\n \n | HTTP Body | \n {\"source\": {\"id\": \"1c920f4241b78a1d483a29f3c24b6c4c\", \"type\": \"VAULT\"}, \"assetType\": \"ETH\", \"destination\": {\"id\": \"55e89d4a644d736b01533a2ea9b32a20\", \"type\": \"VAULT\"}, \"amount\": \"1000.00000000\"} | \n
\n \n | Signing Key (Ed25519 Private Key Seed) | \n 0101010101010101010101010101010101010101010101010101010101010101 | \n
\n \n | Public Key | \n 8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c | \n
\n \n | Signature | \n 4bf42054bf7db1f8a2a2bc83d2a108502ee7a9d2ac7a2738adc2f932922446786fb9be1bd1eb475023296c6cba4ddbe28b04baca4b7521b1f1840a4ffd2b4d0d | \n
\n
\n\n## Reference clients\n\n### Python (with `requests` library)\n\n*Authorize and sign requests*\n\n```python\n\n# https://pypi.org/project/PyNaCl/\n\nfrom nacl import signing\n\nimport time\n\nimport requests\n\n\nclass AnchorageAuth(requests.auth.AuthBase):\n ACCESS_KEY_HEADER = \"Api-Access-Key\"\n SIGNATURE_HEADER = \"Api-Signature\"\n TIMESTAMP_HEADER = \"Api-Timestamp\"\n\n access_key: str\n signing_key: signing.SigningKey\n\n def __init__(self, access_key: str, signing_key_seed: bytes):\n self.access_key = access_key\n self.signing_key = signing.SigningKey(signing_key_seed)\n\n def __call__(self, r: requests.PreparedRequest):\n r.headers[self.ACCESS_KEY_HEADER] = self.access_key\n\n timestamp = str(int(time.time()))\n method = r.method.upper() if r.method else \"GET\"\n body: bytes = bytes()\n if r.body and isinstance(r.body, bytes):\n body = r.body\n elif r.body and isinstance(r.body, str):\n body = bytearray(r.body, \"utf-8\")\n message = b\"\".join(\n [bytearray(timestamp, \"utf-8\"), bytearray(method, \"utf-8\"), bytearray(r.path_url, \"utf-8\"), body]\n )\n signature = self.signing_key.sign(message).signature.hex()\n r.headers[self.SIGNATURE_HEADER] = signature\n r.headers[self.TIMESTAMP_HEADER] = timestamp\n return r\n\n\n# load secrets\n\n# Use the API key generated in the Anchorage Digital Web Dashboard\n\naccess_key = ...\n\n# Use the Ed25519 signing private key\n\nsigning_key_str = ... # load the raw string\n\nsigning_key = bytes(bytearray.fromhex(signing_key_str))\n\ndata = {}\n\nanchorage_auth = AnchorageAuth(access_key, signing_key)\n\nr = requests.post(\"https://api.anchorage.com/v2/transfers\", data=data, auth=anchorage_auth)\n\n```\n\n### Ruby - Reproduce reference signature\n```ruby\n require \"ed25519\"\n require \"net/http\"\n require \"time\"\n\n def hex_to_bin(s)\n [s].pack('H*')\n end\n\n def bin_to_hex(s)\n s.unpack('H*').first\n end\n\n private_key_seed_hex = '0101010101010101010101010101010101010101010101010101010101010101'\n public_key_hex = '8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c'\n key_pair_hex = private_key_seed_hex + public_key_hex\n\n key_pair = hex_to_bin(key_pair_hex)\n\n signing_key = Ed25519::SigningKey.from_keypair(key_pair)\n\n timestamp = '1577880000' # Time.now.to_i.to_s\n\n req = Net::HTTP::Post.new('/v2/transfers?foo=bar&baz=bang')\n req.body = '{\"source\": {\"id\": \"1c920f4241b78a1d483a29f3c24b6c4c\", \"type\": \"VAULT\"}, \"assetType\": \"ETH\", \"destination\": {\"id\": \"55e89d4a644d736b01533a2ea9b32a20\", \"type\": VAULT\"}, \"amount\": \"1000.00000000\"}'\n\n signature = signing_key.sign(timestamp + req.method + req.path + req.body)\n\n req['Api-Access-Key'] = 'YOUR_ACCESS_KEY'\n req['Api-Timestamp'] = timestamp\n req['Api-Signature'] = bin_to_hex(signature)\n\n puts bin_to_hex(signature)\n```\n\n# Errors\n\n\nThe Anchorage Digital API returns standard HTTP error codes for each API request.\n\n\n\n \n | Response Code | \n Description | \n
\n
\n | 200 OK | \n The request was successful. | \n
\n \n | 400 Bad Request | \n The request was improperly formed and could not be understood by the server, often due to invalid syntax, insufficient funds, or a missing required parameter. | \n
\n \n | 401 Unauthorized | \n The request was missing a valid API key. | \n
\n \n | 403 Forbidden | \n The provided API key does not have permission to perform the requested action. | \n
\n \n | 404 Not Found | \n The requested resource does not exist. | \n
\n \n | 409 Conflict | \n The requested resource cannot proceed with the requested action because it is not in the required state. | \n
\n \n | 429 Too Many Requests | \n Too many requests have been sent in a given amount of time. | \n
\n \n \n 500 Internal Server Error\n \n 502 Bad Gateway\n \n 503 Service Unavailable\n \n 504 Gateway Timeout\n | \n Something went wrong on Anchorage’s side. We have been alerted and are working on it. | \n
\n
\n\n\nIn addition to returning HTTP error codes for failed requests, the Anchorage Digital API includes a readable error message describing what went wrong in the response body.\n\n\n\n\n\n# Idempotency\n\n\nCertain endpoints support idempotent requests so that a given request can be safely retried without performing the same operation twice. For example, if a request to transfer funds does not respond due to network issues, you can retry the request using the same idempotent ID to ensure that only one transfer is created.\n\n\nEndpoints that support idempotent requests have an optional `idempotentId` field that can be included in the body of the `POST` request. Provide a unique string using your method of choice (such as a v4 UUID).\n\n\nIf a request is valid, Anchorage Digital will save the request indefinitely. If a subsequent request is received with the same `idempotentId` we will return the previously saved response for that `idempotentId`.\n\n\n# Rate Limits\n\n\nKeys provisioned by an Organization share one common rate limit. API requests are limited to 20 requests per second per Organization, allowing for bursts of up to 100 requests within a single second.\n\n\n# Pagination\n\n\nCursor pagination is used for REST endpoints which return multiple data points. Pagination allows for fetching data after the current page and specifying how many records to return. The `next` cursor is available in responses with the `page` attribute. Requests should use the `next` cursor URL to query subsequent data. Query parameter `afterId` specifies the last record previously retrieved. Some endpoints instead use the `endDate` parameter to specify the end date and older for records to retrieve. Query parameter `limit` specifies the maximum number of records in a response.\n\n## Parameters\n\n\n \n | Parameter | \n Description | \n
\n | afterId | \n Request page after (older than) this pagination id. | \n
\n \n | endDate | \n Request records older than this date (YYYY-MM-DD format). Used for /trading/trades and /trading/settlements resources. | \n
\n \n | limit | \n Maximum number of results requested. Default usually 25, but varies depending on resource. | \n
\n
\n\n## Example\n\n`GET /v2/transfers?afterId=1968b94b09b8a1a8a381775d1f04978c424d891d50e517774bf984297985b471&limit=100`\n\n## Next cursor\n\nThe `next` cursor is a URL which references the last record in a set of records. When queried, the `next` cursor URL will return subsequent records, but otherwise using the same query parameters."
servers:
- url: https://api.anchorage-staging.com/v2
security:
- Api-Access-Key: []
tags:
- description: "These endpoints allow the user to create and retrieve deposit addresses for specific assets.\n\n# Verifying Deposit Addresses\n\nThe addresses REST API endpoints return signatures of the address strings and other metadata that prove the address was generated by Anchorage Digital for your organization. \n\nIt is critical that clients verify the address signature and any accompanying metadata before the address is used, to confirm the address authenticity and integrity.\n\n## Address Signature Schemes\n\nThe API supports two address verification schemes:\n\n- **V1 Address Signatures** - The original scheme involves verifying a signature against a public key that is unique to each organization. This public key remains fixed for the lifetime of the organization and is distributed on request by Anchorage Digital out-of-band. While the public key does not need to be kept confidential, it must be kept tamper-proof.\n- **V2 Address Signatures** - The newer scheme involves verifying a signature against the public key of the leaf certificate of a X509 certificate chain returned alongside the signature, and verifying the certificate chain itself against the Anchorage Digital Address Signing Root CA, provided below, which must be hard-coded by API clients.\n\nYou can determine which scheme an address uses by checking the `signatureVersion` field.\n\n## V1 Address Signature Verification\n\nThe steps for verifying V1 address signatures are as follows:\n\n1. Check the validity of the signature:\n a. Decode the `addressSignaturePayload` field from hex to bytes.\n b. Decode the `signature` field from hex to bytes.\n c. Using the fixed public key for this organization, verify that `signatureBytes` is a valid Ed25519 signature of the `addressSignaturePayloadBytes`.\n2. Verify the signed address matches the address to be used:\n a. Decode the `addressSignaturePayload` field from hex to bytes.\n b. Parse the bytes as a JSON object.\n c. Verify the address to be used matches the value of the `TextAddress` property from the JSON object.\n\n**Note: It is not sufficient to validate the signature without also validating that the address contained in the JSON decoded from the `addressSignaturePayload` matches the address to be used.**\n\n### V1 Signed Payload Fields\n\n- `TextAddress` — The text format of the on-chain address.\n\n```json\n{\n \"TextAddress\": \"2N19AcihQ1a4MxQW658UFHTioUNnMkiHPkw\"\n}\n```\n\n### Sample V1 validation code:\n\n```go\npackage main\n\nimport (\n\t\"crypto/ed25519\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// V1SignedPayload represents the JSON structure in the addressSignaturePayload for V1 signatures\ntype V1SignedPayload struct {\n\tTextAddress string `json:\"TextAddress\"`\n}\n\n// verifyV1AddressSignature verifies a V1 address signature.\n//\n// Parameters:\n// - address: The address string from the API response\n// - addressSignaturePayload: Hex-encoded bytes that were signed\n// - signature: Hex-encoded Ed25519 signature\n// - orgPublicKeyHex: Hex-encoded Ed25519 public key for your organization (obtained out-of-band)\n//\n// Returns an error if verification fails.\nfunc verifyV1AddressSignature(address, addressSignaturePayload, signature, orgPublicKeyHex string) error {\n\t// Step 1: Check the validity of the signature\n\n\t// Decode the addressSignaturePayload from hex to bytes\n\tpayloadBytes, err := hex.DecodeString(addressSignaturePayload)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to decode addressSignaturePayload: %w\", err)\n\t}\n\n\t// Decode the signature from hex to bytes\n\tsignatureBytes, err := hex.DecodeString(signature)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to decode signature: %w\", err)\n\t}\n\n\t// Decode the organization public key from hex\n\tpublicKeyBytes, err := hex.DecodeString(orgPublicKeyHex)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to decode organization public key: %w\", err)\n\t}\n\n\tif len(publicKeyBytes) != ed25519.PublicKeySize {\n\t\treturn fmt.Errorf(\"invalid public key size: got %d bytes, expected %d\", len(publicKeyBytes), ed25519.PublicKeySize)\n\t}\n\n\tpublicKey := ed25519.PublicKey(publicKeyBytes)\n\n\t// Verify the Ed25519 signature\n\tif !ed25519.Verify(publicKey, payloadBytes, signatureBytes) {\n\t\treturn fmt.Errorf(\"signature verification failed\")\n\t}\n\n\t// Step 2: Verify the signed address matches the address to be used\n\n\t// Parse the payload bytes as JSON\n\tvar signedPayload V1SignedPayload\n\tif err := json.Unmarshal(payloadBytes, &signedPayload); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse signed payload: %w\", err)\n\t}\n\n\t// Verify the TextAddress matches\n\tif signedPayload.TextAddress != address {\n\t\treturn fmt.Errorf(\"signed TextAddress does not match: signed=%q, expected=%q\", signedPayload.TextAddress, address)\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\t// Sample API response data\n\taddress := \"2N19AcihQ1a4MxQW658UFHTioUNnMkiHPkw\"\n\taddressSignaturePayload := \"7b225465787441646472657373223a22324e313941636968513161344d78515736353855464854696f554e6e4d6b6948506b77227d\"\n\tsignature := \"b18f6848dc0fef01a069e7ac26046383bf5cd130203994dc2d72b5a9097351b1e8b67115b63124fbc8c16673566416a635913c670b676089339c62a7824baa03\"\n\n\t// Organization public key - obtained out-of-band from Anchorage Digital beforehand\n\t// Unique per Organization, fixed for the lifetime of that Organization\n\t// Must be kept tamper-proof\n\torgPublicKeyHex := \"8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c\"\n\n\t// Verify the signature\n\tif err := verifyV1AddressSignature(address, addressSignaturePayload, signature, orgPublicKeyHex); err != nil {\n\t\tfmt.Printf(\"✗ V1 Address signature verification failed: %v\\n\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"✓ V1 Address signature verified successfully!\")\n\tfmt.Printf(\" Address: %s\\n\", address)\n\tfmt.Println(\"\\nYou may now safely use this address for deposits.\")\n}\n```\n\n## V2 Address Signature Verification\n\nAddresses returned with `signatureVersion` set to `V2` also include a `certChain` field containing an x509 certificate chain in PEM format.\n\nThe steps for verifying V2 address signatures are as follows:\n\n1. Verify the certificate chain:\n a. Parse the `certChain` field as PEM-encoded x509 certificates \n \n **Note: The leaf certificate is at the 0th index, followed by zero or more intermediate certificates. The root cert is excluded from this response. The number of certificates in the chain is subject to change.**\n \n b. Verify the certificate chain from the leaf to the trusted Anchorage Digital Root CA.\n c. Verify all certificates are valid at the current time (both notAfter and notBefore).\n d. Verify the leaf certificate's Subject Alternative Names include `address-provider.anchorage.internal`.\n e. Verify the leaf certificate's KeyUsage includes both `digitalSignature` and `nonRepudiation` (also known as `contentCommitment`).\n f. Extract the public key from the leaf certificate. Currently, we only support ed25519 keys, however this is subject to change the future.\n2. Verify the signature:\n a. Decode the `addressSignaturePayload` field from hex to bytes\n b. Decode the `signature` field from hex to bytes\n c. Using the public key from the leaf certificate, verify that `signatureBytes` is a valid signature of the `addressSignaturePayload` bytes.\n3. Verify the signed details:\n a. Parse the `addressSignaturePayload` bytes as a JSON object\n b. Verify `SignatureExpiresAt` is greater or equal to the current UTC Unix Timestamp\n c. Verify `TextAddress` matches the address to be used\n d. Verify `VaultId` matches your expected Vault ID\n e. Verify `NetworkId` matches the expected network for this address\n\n**Note: The signed payload also includes a `NetworkName` field for human-readable purposes, which does not need to be verified.**\n\n**Note: API clients must not use \"strict\" JSON parsers which will disallow extra properties, as future versions may introduce additional fields.**\n\n**Note: Anchorage Digital will periodically refresh V2 signatures and our Address Signing Root CA before expiration. The deposit address itself will not change. Only the signature, certificate chain and Root CA will be updated.**\n\n### V2 Signed Payload Fields\n\n- `TextAddress` — The text format of the on-chain address.\n- `VaultId` — Identifies the vault this address belongs to.\n- `NetworkId` — Identifies the network that this address can receive deposits on.\n- `NetworkName` — A human readable version of the `NetworkId`.\n- `SignatureExpiresAt` — The time after which the signature should not be trusted.\n\n```json\n{\n \"VaultId\": \"dae6089e7c0836705f0562af0f1e4e1f\",\n \"TextAddress\": \"bcrt1q709skemgf5skpsnysvgme2s3ztehkutl390yl0wp29lnmum5uw7qg0qrwm\",\n \"NetworkName\": \"Bitcoin Regnet\",\n \"NetworkId\": \"BTC_R\",\n \"SignatureExpiresAt\": 1769450713\n}\n```\n\n### Anchorage Digital Address Signing Root CAs\n\nClients are encouraged to hard-code the appropriate Root CA value for the environment they are making API requests against. It is essential that this value be tamper-proof.\n\n- Production Environment:\n\n```\n-----BEGIN CERTIFICATE-----\nMIIBXTCCAQ+gAwIBAgIUQZI+MSvYTXQHra+3OAKnwAMzotUwBQYDK2VwMCAxHjAcBgNVBAMMFWNhLmFuY2hvcmFnZS5pbnRlcm5hbDAeFw0yNjAxMjYwMDAwMDBaFw0yNzAxMjYwMDAwMDBaMCAxHjAcBgNVBAMMFWNhLmFuY2hvcmFnZS5pbnRlcm5hbDAqMAUGAytlcAMhADTh1nctgIHtAKNW8ww/bY606pJ3OP2dyZYcQrU2kG5jo1swWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAUBgorBgEEAYaNHwEBBAYWBHJvb3QwIAYDVR0RBBkwF4IVY2EuYW5jaG9yYWdlLmludGVybmFsMAUGAytlcANBANkkdudEjH9RTKbRAxrRXyMSS/TgmdSrAVYOZzoRDJlyc+5oD+a0pmmwWVe86xZi37YbN1GzVlXcJAPpV6ceEQU=\n-----END CERTIFICATE-----\n```\n\n- Staging Environment:\n\n```\n-----BEGIN CERTIFICATE-----\nMIIBXDCCAQ6gAwIBAgITOfTQ4rYUsghgvdl8YCJSC67uGDAFBgMrZXAwIDEeMBwGA1UEAwwVY2EuYW5jaG9yYWdlLmludGVybmFsMB4XDTI2MDEyNDAwMDAwMFoXDTI3MDEyNDAwMDAwMFowIDEeMBwGA1UEAwwVY2EuYW5jaG9yYWdlLmludGVybmFsMCowBQYDK2VwAyEAPlBo2/+kPPL0WRpT+B/yHsU25AN/M6HP2bzC61yHb4ajWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMBQGCisGAQQBho0fAQEEBhYEcm9vdDAgBgNVHREEGTAXghVjYS5hbmNob3JhZ2UuaW50ZXJuYWwwBQYDK2VwA0EAYsJxVI9n42liCF9f+Ou7uuC1QGFwaHwFsfOm0WFofSlE1trWqzj4ruzjPYSRJc8Ht2A7XCAfXkG0mzKpL/wQDg==\n-----END CERTIFICATE-----\n```\n\n### Sample V2 validation code:\n\n```go\npackage main\n\nimport (\n\t\"crypto/ed25519\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// V2SignedPayload represents the JSON structure in the addressSignaturePayload for V2 signatures\ntype V2SignedPayload struct {\n\tTextAddress string `json:\"TextAddress\"`\n\tVaultId string `json:\"VaultId\"`\n\tNetworkId string `json:\"NetworkId\"`\n\tNetworkName string `json:\"NetworkName\"`\n\tSignatureExpiresAt int64 `json:\"SignatureExpiresAt\"` // Unix timestamp\n}\n\n// verifyV2AddressSignature verifies a V2 address signature.\n//\n// Parameters:\n// - now: The \"current\" time. Note that conforming implementations must use\n// a trusted source for the current time.\n// - address: The address string from the API response\n// - addressSignaturePayload: Hex-encoded bytes that were signed\n// - signature: Hex-encoded signature\n// - certChainPEM: PEM-encoded certificate chain (leaf first, then intermediates)\n// - rootCAPEM: PEM-encoded Root CA certificate (hard-coded by client)\n// - expectedVaultId: Your Vault ID to verify against the signed VaultId\n// - expectedNetworkId: Expected network ID for this address (e.g., \"BTC\", \"ETH\")\n//\n// Returns an error if verification fails.\nfunc verifyV2AddressSignature(\n\tnow time.Time,\n\taddress, addressSignaturePayload, signature, certChainPEM, rootCAPEM, expectedVaultId, expectedNetworkId string,\n) error {\n\t// Step 1: Verify the certificate chain\n\n\t// Parse the certificate chain from PEM\n\tcerts, err := parsePEMCertificates([]byte(certChainPEM))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse certificate chain: %w\", err)\n\t}\n\n\tif len(certs) == 0 {\n\t\treturn fmt.Errorf(\"certificate chain is empty\")\n\t}\n\n\tleafCert := certs[0]\n\tvar intermediateCerts []*x509.Certificate\n\tif len(certs) > 1 {\n\t\tintermediateCerts = certs[1:]\n\t}\n\n\t// Parse the Root CA\n\trootCACerts, err := parsePEMCertificates([]byte(rootCAPEM))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse Root CA: %w\", err)\n\t}\n\tif len(rootCACerts) != 1 {\n\t\treturn fmt.Errorf(\"expected exactly one Root CA certificate, got %d\", len(rootCACerts))\n\t}\n\trootCA := rootCACerts[0]\n\n\t// Verify the leaf certificate's KeyUsage includes both\n\t// digitalSignature and nonRepudiation (AKA contentCommitment)\n\tif leafCert.KeyUsage&x509.KeyUsageDigitalSignature == 0 {\n\t\treturn fmt.Errorf(\"leaf certificate KeyUsage missing DigitalSignature\")\n\t}\n\tif leafCert.KeyUsage&x509.KeyUsageContentCommitment == 0 {\n\t\treturn fmt.Errorf(\"leaf certificate KeyUsage missing NonRepudiation (ContentCommitment)\")\n\t}\n\n\t// Verify the certificate chain from leaf to Root CA\n\troots := x509.NewCertPool()\n\troots.AddCert(rootCA)\n\n\tintermediates := x509.NewCertPool()\n\tfor _, cert := range intermediateCerts {\n\t\tintermediates.AddCert(cert)\n\t}\n\n\t// NOTE: Not all x509 libraries are created equal and are not\n\t// guaranteed to verify exactly the same things!\n\t//\n\t// Always review the library you plan to use and ensure it covers the\n\t// checks described in the User Guide!\n\t//\n\t// For example, the Go implementation checks all Certificates for\n\t// temporal validity (notBefore and notAfter against CurrentTime), for\n\t// valid signatures up the chain, and checks that the Subject\n\t// Alternative Names include the values in DNSNames below.\n\t//\n\t// However it does not check the KeyUsage bits, hence the additional\n\t// checks above.\n\topts := x509.VerifyOptions{\n\t\tDNSNames: []string{\"address-provider.anchorage.internal\"},\n\t\tRoots: roots,\n\t\tIntermediates: intermediates,\n\t\tCurrentTime: now,\n\t\t// NOTE: This allows for any Extended Key Usage, but does not\n\t\t// check the Key Usage bits, hence the additional checks above.\n\t\tKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},\n\t}\n\tif _, err := leafCert.Verify(opts); err != nil {\n\t\treturn fmt.Errorf(\"certificate chain verification failed: %w\", err)\n\t}\n\n\t// Extract the public key from the leaf certificate\n\tleafPublicKey, ok := leafCert.PublicKey.(ed25519.PublicKey)\n\tif !ok {\n\t\treturn fmt.Errorf(\"leaf certificate does not use Ed25519 (got type %T)\", leafCert.PublicKey)\n\t}\n\n\t// Step 2: Verify the signature\n\n\t// Decode the addressSignaturePayload from hex to bytes\n\tpayloadBytes, err := hex.DecodeString(addressSignaturePayload)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to decode addressSignaturePayload: %w\", err)\n\t}\n\n\t// Decode the signature from hex to bytes\n\tsignatureBytes, err := hex.DecodeString(signature)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to decode signature: %w\", err)\n\t}\n\n\t// Verify the signature using the leaf certificate's public key\n\tif !ed25519.Verify(leafPublicKey, payloadBytes, signatureBytes) {\n\t\treturn fmt.Errorf(\"signature verification failed\")\n\t}\n\n\t// Step 3: Verify the signed details\n\n\t// Parse the payload bytes as JSON\n\tvar signedPayload V2SignedPayload\n\tif err := json.Unmarshal(payloadBytes, &signedPayload); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse signed payload: %w\", err)\n\t}\n\n\t// Verify SignatureExpiresAt is not in the past\n\tif now.Unix() > signedPayload.SignatureExpiresAt {\n\t\texpiryTime := time.Unix(signedPayload.SignatureExpiresAt, 0)\n\t\treturn fmt.Errorf(\"signature has expired at %s\", expiryTime)\n\t}\n\n\t// Verify TextAddress matches\n\tif signedPayload.TextAddress != address {\n\t\treturn fmt.Errorf(\"signed TextAddress does not match: signed=%q, expected=%q\",\n\t\t\tsignedPayload.TextAddress, address)\n\t}\n\n\t// Verify VaultId matches\n\tif signedPayload.VaultId != expectedVaultId {\n\t\treturn fmt.Errorf(\"signed VaultId does not match: signed=%q, expected=%q\",\n\t\t\tsignedPayload.VaultId, expectedVaultId)\n\t}\n\n\t// Verify NetworkId matches\n\tif signedPayload.NetworkId != expectedNetworkId {\n\t\treturn fmt.Errorf(\"signed NetworkId does not match: signed=%q, expected=%q\",\n\t\t\tsignedPayload.NetworkId, expectedNetworkId)\n\t}\n\n\treturn nil\n}\n\n// parsePEMCertificates parses PEM-encoded certificates and returns them as a slice\nfunc parsePEMCertificates(pemData []byte) ([]*x509.Certificate, error) {\n\tvar certs []*x509.Certificate\n\n\tfor {\n\t\tblock, rest := pem.Decode(pemData)\n\t\tif block == nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif block.Type != \"CERTIFICATE\" {\n\t\t\tpemData = rest\n\t\t\tcontinue\n\t\t}\n\n\t\tcert, err := x509.ParseCertificate(block.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse certificate: %w\", err)\n\t\t}\n\n\t\tcerts = append(certs, cert)\n\t\tpemData = rest\n\t}\n\n\treturn certs, nil\n}\n\nfunc main() {\n\t// Sample API response data\n\taddress := \"bcrt1q709skemgf5skpsnysvgme2s3ztehkutl390yl0wp29lnmum5uw7qg0qrwm\"\n\taddressSignaturePayload := \"7b225661756c744964223a226461653630383965376330383336373035663035363261663066316534653166222c225465787441646472657373223a22626372743171373039736b656d676635736b70736e797376676d653273337a7465686b75746c333930796c30777032396c6e6d756d357577377167307172776d222c224e6574776f726b4e616d65223a22426974636f696e205265676e6574222c224e6574776f726b4964223a224254435f52222c225369676e6174757265457870697265734174223a313736393435303731337d\"\n\tsignature := \"951eb2fb560e660aa9c3d1ccd120d3ad1a19d90d8747347057e48bf174330eb386089e3232d822fd66b8183cce8059c91183afde299b920a0e0c05c5b167360e\"\n\tcertChainPEM := `-----BEGIN CERTIFICATE-----\nMIIBYTCCAROgAwIBAgIUMLKt+K9eFku+P7BbefE1xAHg0hcwBQYDK2VwMAAwHhcNMjYwMTI2MTcwNDEzWhcNMjcwMTI2MTcwNTEzWjAuMSwwKgYDVQQDEyNhZGRyZXNzLXByb3ZpZGVyLmFuY2hvcmFnZS5pbnRlcm5hbDAqMAUGAytlcAMhAPsgM70aWFYsZaLHawtYJpl42BkiTLyCq96+OXe4FxrVo3EwbzAOBgNVHQ8BAf8EBAMCBsAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBS0usSFeB2gjC+wcowtxN3MeKSH7zAuBgNVHREEJzAlgiNhZGRyZXNzLXByb3ZpZGVyLmFuY2hvcmFnZS5pbnRlcm5hbDAFBgMrZXADQQCXmvIkuPnUgCHxWmFmzvgWdv9lUlt84oZCel+OeJW9n8PR88tGxAcD1E3+KDBXVpO0GcRA0W9+xqqICAo2ROEJ\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIBKDCB26ADAgECAhRGsD05KldIse+uIEa976AijTqlxjAFBgMrZXAwADAeFw0yNjAxMjYxNzA0MTNaFw0yNzAxMjYxNzA1MTNaMAAwKjAFBgMrZXADIQCNpyY5Sr21FHNvvLkBKG8AEMKdhqtajmV5d2QaZlmtAqNnMGUwDgYDVR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLS6xIV4HaCML7ByjC3E3cx4pIfvMCAGA1UdEQEB/wQWMBSCEmFuY2hvcmFnZS5pbnRlcm5hbDAFBgMrZXADQQCIgw6kLMIwhd3ACjG03cJ5z/ZZp8aXXycFq2ZC9TLhieJ3rncyMH6ZdyJ3Ai1eVaHs4vnDCv54Vdh83vvSky4K\n-----END CERTIFICATE-----\n`\n\n\t// NOTE: This is a FAKE Root CA used just for this example.\n\t// NOTE: Conforming client implementations should hard-code the real\n\t// Anchorage Digital Address Signing Root CA for the environment they\n\t// are making requests to.\n\trootCAPEM := `-----BEGIN CERTIFICATE-----\nMIIBGzCBzqADAgECAhQ2qQwArneTuF0dbNDs8i/ExuyW2DAFBgMrZXAwADAeFw0yNjAxMjYxNzA0MTNaFw0yNzAxMjYxNzA1MTNaMAAwKjAFBgMrZXADIQB+gEnytXKnuAMonIWGWnB0qyTqa0aw3l9u5VRbu86UgaNaMFgwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOj64tL1teJHkojsiblnnK34Tw+EMBYGA1UdEQEB/wQMMAqCCGludGVybmFsMAUGAytlcANBAAg2IcVEXmWKSivhUNSatNfMmASxi83QscIuyP/sIW2sRIuCqQJoo9lN6TaxzyV62cQMzthFOCZcgRE+k0JV7Ao=\n-----END CERTIFICATE-----\n`\n\n\t// Your Vault ID - obtained from your application context\n\texpectedVaultId := \"dae6089e7c0836705f0562af0f1e4e1f\"\n\n\t// Expected network ID for this address\n\texpectedNetworkId := \"BTC_R\"\n\n\t// Implementations should use the actual current time\n\t// now := time.Now()\n\tnow := time.Unix(1769450600, 0) // Fake time so that this example passes.\n\n\t// Verify the signature\n\tif err := verifyV2AddressSignature(\n\t\tnow,\n\t\taddress,\n\t\taddressSignaturePayload,\n\t\tsignature,\n\t\tcertChainPEM,\n\t\trootCAPEM,\n\t\texpectedVaultId,\n\t\texpectedNetworkId,\n\t); err != nil {\n\t\tfmt.Printf(\"✗ V2 Address signature verification failed: %v\\n\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"✓ V2 Address signature verified successfully!\")\n\tfmt.Printf(\" Address: %s\\n\", address)\n\tfmt.Printf(\" Vault ID: %s\\n\", expectedVaultId)\n\tfmt.Printf(\" Network: %s\\n\", expectedNetworkId)\n\tfmt.Println(\"\\nYou may now safely use this address for deposits.\")\n}\n```\n"
name: Addresses
paths:
/vaults/{vaultId}/addresses:
get:
operationId: getAddresses
summary: List all addresses for an asset
description: 'Permissions required: **Read vault activity** or **Create address**
Get all addresses for the asset type within the specified `vaultId`.
'
parameters:
- name: vaultId
in: path
description: The ID of the vault containing the asset type
required: true
schema:
type: string
- name: assetType
in: query
description: The asset type to display addresses for
required: true
schema:
type: string
- name: afterId
in: query
description: When paginating this is used to provide the starting point for the page to fetch (retrieved from the previous response body)
schema:
type: string
- name: limit
in: query
description: Maximum number of results to return per query
schema:
type: integer
format: int64
default: 25
maximum: 100
minimum: 1
responses:
'200':
description: List of signed addresses for this asset
content:
application/json:
schema:
$ref: '#/components/schemas/VaultsAddressesResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'401':
description: Unauthenticated
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'429':
description: Too Many Requests
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
tags:
- Addresses
x-security:
Read vault activity: checkSourceVault
Create address: checkSourceVault
/wallets/{walletId}/addresses:
post:
operationId: provisionWalletAddress
summary: Provision a deposit address for a wallet
description: 'Permissions required: **Create address**
This endpoint allows you to request an asynchronous provision of a signed deposit address for a given wallet. For UTXO-based wallets a new address will always be provisioned upon request. For account-based wallets only one address may be provisioned per wallet and subsequent attempts will return the previously provisioned address.
Newly requested addresses may not be available for several minutes once requested.
'
parameters:
- name: walletId
in: path
description: An ID uniquely identifying an Anchorage Digital wallet
required: true
schema:
type: string
responses:
'200':
description: Provisioned address
content:
application/json:
schema:
$ref: '#/components/schemas/WalletsAddressesResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'401':
description: Unauthenticated
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'422':
description: Provision address in progress
content:
application/json:
examples:
response:
value:
errorType: UnprocessableEntity
message: An address for this wallet is being created and is not yet available. Please try again in 5 minutes.
schema:
$ref: '#/components/schemas/ErrorDetails'
'429':
description: Too Many Requests
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails'
tags:
- Addresses
x-security:
Create address: checkSourceVault
/addresses:
get:
operationId: listAddresses
summary: List Addresses
description: 'Permissions required: **Read vault activity**
List all addresses across wallets in the organization.'
parameters:
- name: after
in: query
description: When paginating this is used to provide the starting point for the page to fetch (retrieved from the previous response body)
schema:
type: string
- name: address
in: query
description: Filter by specific blockchain address
required: false
schema:
type: string
- name: networkId
in: query
description: A unique string identifying a combination of blockchain and environment (testnet, mainnet).
required: false
schema:
type: string
- name: vaultId
in: query
description: Filter by vault ID
required: false
schema:
type: string
- name: walletId
in: query
description: Filter by wallet ID
required: false
schema:
type: string
- name: batchId
in: query
description: Filter by batch creation ID
required: false
schema:
type: string
- name: publicKey
in: query
description: Filter by public key string
required: false
schema:
type: string
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/ListAddressesResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'401':
description: Unauthenticated
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'429':
description: Too Many Requests
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
tags:
- Addresses
/batch/addresses:
post:
operationId: createAddressesBatch
summary: Create Addresses Batch
description: 'Permissions required: **Create addresses**
Create multiple blockchain addresses in a single batch operation. This endpoint returns immediately with a batchId.
Poll GET /batch/addresses/{batchId} to check the status and retrieve results once the batch is complete.'
parameters: []
requestBody:
description: Batch address creation request
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAddressesBatchRequest'
responses:
'202':
description: Batch address creation initiated
content:
application/json:
schema:
$ref: '#/components/schemas/CreateBatchResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'401':
description: Unauthenticated
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'429':
description: Too Many Requests
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
tags:
- Addresses
/batch/addresses/{batchId}:
get:
operationId: getAddressesBatchStatus
summary: Get Addresses Batch Status
description: 'Permissions required: **Read vault activity**
Get the status and results of a batch address creation operation.'
parameters:
- name: batchId
in: path
description: The unique identifier for the batch operation
required: true
schema:
type: string
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/BatchStatusResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'401':
description: Unauthenticated
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'429':
description: Too Many Requests
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
tags:
- Addresses
/addresses/validate-destination:
post:
operationId: validateDestinationAddress
summary: Validate Destination Address
description: 'Permissions required: **None**
Validate that a given address for a given networkId is valid as a transfer destination.
Returns 200 if the address is valid, or an invalid request error if it is not.'
parameters: []
requestBody:
description: Destination address validation request
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ValidateDestinationAddressRequest'
responses:
'200':
description: Address is valid as a transfer destination
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'401':
description: Unauthenticated
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'429':
description: Too Many Requests
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDetails_2'
tags:
- Addresses
components:
schemas:
VaultsAddressesResponse:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/SignedAddress'
page:
$ref: '#/components/schemas/Page'
required:
- data
- page
title: VaultsAddressesResponse
ErrorType:
description: The type of error returned.
type: string
enum:
- InternalError
- InvalidRequest
- Unauthenticated
- Forbidden
- NotFound
- Conflict
- UnprocessableEntity
- TooManyRequests
- ServiceUnavailable
- QuoteExpired
- InsufficientFunds
- NotImplemented
title: ErrorType
WalletAddress:
description: "A blockchain address in a wallet. Each address includes a cryptographic signature that proves the address was generated by Anchorage Digital for your organization.\n\n## Signature Verification\n\nThe signature allows you to verify that an address string is authentic before using it for sensitive operations (like withdrawals to that address).\n\nCheck the `signatureVersion` field to determine which verification scheme applies. For complete verification steps, sample code, and Root CA values, see the Address verification guide docs.\n\n### V1 Address Signatures\n\nThe steps for verifying V1 address signatures are as follows:\n1. Check the validity of the signature\n - Decode the `signedJson` field from hex to bytes\n - Decode the `signature` field from hex to bytes\n - Using your organization's verification public key (provided by Anchorage Digital), verify that the signature bytes are a valid Ed25519 signature of the signedJson bytes\n2. Verify the address that was signed matches the one that will be used\n - Decode the `signedJson` field from hex to bytes\n - Parse the bytes as a JSON object (e.g., `{\"TextAddress\":\"0x123...\"}`)\n - Verify the address to be used matches the value of the `TextAddress` property from the JSON object\n\n**Important**: It is not sufficient to validate the signature without also validating that the address signed matches the address to be used.\n\n**Note**: The JSON object may contain additional properties in future versions. API clients must not use strict JSON parsers which disallow extra, unknown properties.\n\n**Verification Public Key**: Contact your Anchorage Digital representative to obtain the verification public key for your organization. This key is unique per organization and fixed for the lifetime of that organization.\n\n### V2 Address Signatures\n\nAddresses with `signatureVersion` set to `V2` also include a `certChain` field containing an X.509 certificate chain in PEM format.\n\nThe steps for verifying V2 address signatures are as follows:\n1. Verify the certificate chain:\n - Parse the `certChain` field as PEM-encoded X.509 certificates (leaf at index 0, then intermediates)\n - Verify the chain up to the hard-coded Anchorage Digital Address Signing Root CA (see Address verification guide docs)\n - Verify all certificates are valid at the current time (both notAfter and notBefore)\n - Verify the leaf certificate's Subject Alternative Names include `address-provider.anchorage.internal`\n - Verify the leaf certificate's KeyUsage includes both `digitalSignature` and `nonRepudiation` (also known as `contentCommitment`)\n - Extract the public key from the leaf certificate\n2. Verify the signature:\n - Decode the `signedJson` field from hex to bytes\n - Decode the `signature` field from hex to bytes\n - Using the public key from the leaf certificate, verify that `signature` is a valid signature of the `signedJson` bytes\n3. Verify the signed details:\n - Parse the `signedJson` bytes as JSON\n - Verify `SignatureExpiresAt` is greater than or equal to the current UTC Unix timestamp\n - Verify `TextAddress` matches the address to be used\n - Verify `VaultId` matches your expected Vault ID\n - Verify `NetworkId` matches the expected network for this address\n\n**Note**: API clients must not use strict JSON parsers which disallow unknown properties, as future versions may add new fields to the signed payload.\n\n**Note**: Anchorage Digital periodically refreshes V2 signatures and the certificate chain before expiration. The address itself does not change."
type: object
properties:
address:
description: A unique string identifying an account on a blockchain. This is sometimes dervied from a public key and sometimes decided by the blockchain.
type: string
certChain:
description: PEM-encoded X.509 certificate chain used for V2 signature verification. The leaf certificate is at index 0, followed by zero or more intermediate certificates. The root CA is excluded and must be hard-coded by clients. Only present when `signatureVersion` is `V2`.
type: string
networkId:
description: A unique string identifying a combination of blockchain and environment (testnet, mainnet).
type: string
publicKey:
description: 'The public key that the blockchain address is derived from. Note: This is NOT the key used to verify the signature field - that requires a separate organization-wide verification public key provided by Anchorage Digital.'
type: string
signature:
description: Hex-encoded Ed25519 signature of the `signedJson` bytes. For V1 signatures, verify against your organization's fixed public key. For V2 signatures, verify against the leaf certificate's public key from the `certChain` field.
type: string
example: 1642000aa9cca8e8610981aefbdb204b361c9dca3fa067b88fdacfba7a0f620d721378a33f4bbadad3923e633a4d712646d1e8e314e9fcb4aa4102c0581f6503
signatureVersion:
description: Version of the address signature scheme used. Check this field to determine how to verify the `signature` field.
allOf:
- $ref: '#/components/schemas/WalletAddressSignatureVersion'
signedJson:
description: Hex-encoded bytes that were signed to produce the `signature`. Decode from hex to get the signed content. For V1 signatures, the content is a JSON object with a `TextAddress` field. For V2 signatures, the content is a JSON object with `TextAddress`, `VaultId`, `NetworkId`, `NetworkName`, and `SignatureExpiresAt` fields.
type: string
example: 7b225465787441646472657373223a22307831323334227d
walletId:
description: The wallet ID containing this address.
type: string
required:
- address
- networkId
- publicKey
- signedJson
- signature
- walletId
- signatureVersion
ListAddressesResponse:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/WalletAddress'
page:
$ref: '#/components/schemas/Page_2'
required:
- data
- page
Result:
type: string
enum:
- RETURNING_UNUSED_ADDRESS
- RETURNING_USED_ADDRESS
title: Result
CreateBatchResponse:
description: Response from creating a batch operation
type: object
properties:
batchId:
description: The unique identifier for the batch operation. Poll the corresponding batch status endpoint to retrieve results.
type: string
example: batch_abc123xyz
required:
- batchId
ErrorDetails_2:
type: object
properties:
message:
description: A human-readable message providing more details about the error.
type: string
example: Missing required field 'amount'.
required:
- message
title: ErrorDetails
Page:
description: Pagination info
type: object
properties:
next:
description: URL to use to query for the next page or null if no additional results are available
type: string
example:
nullable: true
x-omitempty: false
title: Page
WalletAddressSignatureVersion:
description: 'Version of the address signature scheme used.
* `V1` - The original scheme. Verify the `signature` against your organization''s fixed Ed25519 public key (provided by Anchorage Digital out-of-band).
* `V2` - The newer scheme. Verify the `signature` against the leaf certificate''s public key from the `certChain` field, and verify the certificate chain against the Anchorage Digital Address Signing Root CA.'
type: string
enum:
- V1
- V2
ValidateDestinationAddressRequest:
description: Request to validate an address as a transfer destination
type: object
properties:
address:
description: A unique string identifying an account on a blockchain. This is sometimes dervied from a public key and sometimes decided by the blockchain.
type: string
memo:
description: Optional memo/tag/destination tag (required by some blockchain networks)
type: string
maxLength: 256
nullable: true
networkId:
description: A unique string identifying a combination of blockchain and environment (testnet, mainnet).
type: string
required:
- address
- networkId
WalletsAddressesResponse:
type: object
properties:
data:
$ref: '#/components/schemas/Data'
required:
- data
title: WalletsAddressesResponse
Page_2:
description: Pagination info
type: object
properties:
endCursor:
description: Submit this parameter in the "after" field to fetch the next page.
type: string
nullable: true
title: Page
Data:
type: object
properties:
result:
$ref: '#/components/schemas/Result'
signedAddress:
$ref: '#/components/schemas/SignedAddress'
required:
- result
- signedAddress
title: Data
ErrorDetails:
type: object
properties:
errorType:
$ref: '#/components/schemas/ErrorType'
message:
description: A human-readable message providing more details about the error.
type: string
example: Missing required field 'amount'.
required:
- errorType
- message
title: ErrorDetails
CreateAddressesBatchRequest:
description: Request to create multiple addresses in a batch within a single wallet
type: object
properties:
count:
description: Number of addresses to create
type: integer
minimum: 1
idempotencyKey:
description: Client-provided idempotency key to ensure request is processed only once
type: string
example: e763a50d-aa82-4ec7-b5a3-89ad0462d248
maxLength: 128
walletId:
description: The wallet ID in which to create addresses
type: string
additionalProperties: false
required:
- walletId
- count
BatchStatusResponse:
description: Status of a batch operation
type: object
properties:
batchId:
description: The unique identifier for the batch operation
type: string
status:
description: Overall status of the batch operation
type: string
enum:
- PENDING
- COMPLETED
- FAILED
required:
- batchId
- status
SignedAddress:
type: object
properties:
address:
description: A crypto address for the intended asset
type: string
example: 2N19AcihQ1a4MxQW658UFHTioUNnMkiHPkw
addressId:
description: An id uniquely identifying an Anchorage Digital address.
type: string
example: a33f83d96ca95cac000a344aa478a8b8
addressSignaturePayload:
description: The hex-encoding of the bytes that were signed.
type: string
example: 7b225465787441646472657373223a22324e313941636968513161344d78515736353855464854696f554e6e4d6b6948506b77227d
certChain:
description: An x509 certificate chain in PEM format. When present, indicates that this address uses a V2 signature scheme where the signature is verified against the public key in the leaf certificate, and the certificate chain must verify up to a trusted Anchorage Digital Root CA. The Root CA is provided out-of-band. When absent, the signature is verified against the Organization Public Key (V1 signature scheme).
type: string
example: '-----BEGIN CERTIFICATE-----
MIIBkTCCATigAwIBAgIQExample...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBkTCCATigAwIBAgIQExample...
-----END CERTIFICATE-----
'
signature:
description: A hex-encoded signature of the addressSignaturePayload
type: string
example: 1642000aa9cca8e8610981aefbdb204b361c9dca3fa067b88fdacfba7a0f620d721378a33f4bbadad3923e633a4d712646d1e8e314e9fcb4aa4102c0581f6503
signatureVersion:
description: The address signature scheme used for this address
type: string
example: V2
enum:
- V1
- V2
walletId:
description: An ID uniquely identifying an Anchorage Digital wallet
type: string
example: a33f83d96ca95cac000a344aa478a8b8
required:
- address
- addressId
- addressSignaturePayload
- signature
title: SignedAddress
securitySchemes:
Api-Access-Key:
type: apiKey
name: Api-Access-Key
in: header
description: An API key associated with a security role
x-tagGroups:
- name: Under Development
tags:
- Collateral Management
- Holds
- Deposit Attribution
- Onboarding
- Trusted Destinations
- Atlas Settlement Network
- Tax
- name: API Endpoints
tags:
- Addresses
- Asset Types
- Transactions
- Transfers
- Wallets
- Vaults
- Vesting
- Tagging
- Subaccounts
- Stablecoins
- Webhook Notifications
- API Key
- Statements
- Tax Reporting
- Trading
- name: Models
tags:
- Transfer Model
- Transaction Model
- Vault Model
- Deposit Attribution Model