# Update admin quorum threshold Source: https://developers.fireblocks.com/api-reference/admin-quorum/update-admin-quorum-threshold https://swagger.fireblocks.com/openapi.yaml put /admin_quorum Update admin quorum threshold # Create API Key Source: https://developers.fireblocks.com/api-reference/api-user/create-api-key https://swagger.fireblocks.com/openapi.yaml post /management/api_users Create a new API key in your workspace. Learn more about Fireblocks API Keys management in the following [guide](https://developers.fireblocks.com/docs/manage-api-keys). Endpoint Permission: Admin, Non-Signing Admin. # Get API Keys Source: https://developers.fireblocks.com/api-reference/api-user/get-api-keys https://swagger.fireblocks.com/openapi.yaml get /management/api_users List all API keys in your workspace. - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Endpoint Permission: Admin, Non-Signing Admin. # Issue API user pairing token Source: https://developers.fireblocks.com/api-reference/api-user/issue-api-user-pairing-token https://swagger.fireblocks.com/openapi.yaml post /management/api_users/{userId}/pairing_token Issues a device pairing token for the given user and returns the user's info along with the token. - The API user must be in PENDING_ACTIVATION status. If the user is already set up (enabled), the request is rejected with a 409 Conflict. - Please note that this endpoint is available only for API keys with Owner/Admin/Non Signing Admin permissions. - This is the same pairing token that the API Co-signer requests during installation and when you pair an additional API user with an existing Co-signer. You can use this endpoint instead of copying the token from Pair API user in the Console. The token is valid for one hour. # Approve an approval request Source: https://developers.fireblocks.com/api-reference/approvals-beta/approve-an-approval-request https://swagger.fireblocks.com/openapi.yaml post /approvals/{requestId}/approve Approve a pending approval request as the authenticated API user. The caller signs the request's signable data with the private key of a registered approval API key and submits the base64url-encoded signature, optionally with the key ID. The server verifies the signature against the registered public key — using the given key ID, or matching against all of the user's registered keys when the key ID is omitted — and advances the approval quorum. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin. # Delete an approval key Source: https://developers.fireblocks.com/api-reference/approvals-beta/delete-an-approval-key https://swagger.fireblocks.com/openapi.yaml delete /management/api_users/{userId}/approval_keys/{keyId} Delete (revoke) an approval public key for the specified API user. The deletion may require approval: it always does for the API user's last key or another user's key. In that case the response carries `ccrIdPendingDeletion`, the key reads as `APPROVAL_API_KEY_STATUS_PENDING_DELETION` and stays active until the request is approved. A rejected request leaves the key enabled. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin. # Get a single approval request Source: https://developers.fireblocks.com/api-reference/approvals-beta/get-a-single-approval-request https://swagger.fireblocks.com/openapi.yaml get /approvals/{requestId} Retrieve full detail for a single approval request by ID, including the payload to sign and, when requested, the request's `quorumStatus`. Because this endpoint addresses one request, it accepts `quorumStatusMode=FULL`, which adds the participating approvers and their individual approval state. `userStatus` reflects the authenticated user by default. Pass `userId` to report it for another user instead. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin, Security Auditor. # List approval keys Source: https://developers.fireblocks.com/api-reference/approvals-beta/list-approval-keys https://swagger.fireblocks.com/openapi.yaml get /management/api_users/{userId}/approval_keys List the approval public keys registered for the specified API user. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin, Security Auditor. # List approval requests Source: https://developers.fireblocks.com/api-reference/approvals-beta/list-approval-requests https://swagger.fireblocks.com/openapi.yaml get /approvals Retrieve the pending approval requests the authenticated API user is eligible to act on, including requests the user has already approved that are still pending overall. The response is scoped to the authenticated user by default. Pass `userId` to read another user's queue, or `includeAllUsers=true` to read every pending request in the workspace. Both require an Admin, Non-Signing Admin, Security Admin or Security Auditor role and are rejected with 403 otherwise. Set `quorumStatusMode=SUMMARY` to include each request's approval thresholds and counts. The per-approver breakdown is available only when fetching a single request — see `GET /approvals/{requestId}`. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin, Security Auditor. # Register an approval key Source: https://developers.fireblocks.com/api-reference/approvals-beta/register-an-approval-key https://swagger.fireblocks.com/openapi.yaml post /management/api_users/{userId}/approval_keys Register an approval public key for an API user, used to sign approval requests. Up to 2 active keys are supported per API user. Returns the server-generated key ID used for deletion. The `userId` must be the authenticated API user's own ID. Registering a key for another user is not supported and is rejected. Registration may require approval. In that case the response carries `ccrIdPendingRegistration`, the key reads as `APPROVAL_API_KEY_STATUS_PENDING_REGISTRATION` and cannot sign until the request is approved. A rejected request removes the key. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin. # Reject an approval request Source: https://developers.fireblocks.com/api-reference/approvals-beta/reject-an-approval-request https://swagger.fireblocks.com/openapi.yaml post /approvals/{requestId}/reject Reject a pending approval request as the authenticated API user. No signature is required (unlike approve). The caller must be eligible to act on the request; rejecting finalizes the request as rejected per the approval policy. Endpoint Permission: Owner, Admin, Non-Signing Admin, Approver, Signer, Security Admin. # Get audit logs Source: https://developers.fireblocks.com/api-reference/audit-logs/get-audit-logs https://swagger.fireblocks.com/openapi.yaml get /management/audit_logs Retrieve audit log events for the workspace with optional filtering, date range, sorting, and cursor-based pagination. Filters within the same field are combined as OR (e.g. category=Administration&category=Security returns events in either category). Filters across different fields are combined as AND. **Deprecated parameters:** `timePeriod` and `cursor` remain functional for backward compatibility but new integrations should use `startTime`/`endTime` and `pageCursor` instead. Endpoint Permission: Admin, Non-Signing Admin, Auditor, Security Admin, Security Auditor. # Activate a blockchain (triggers activation workflow) Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/activate-a-blockchain-triggers-activation-workflow https://swagger.fireblocks.com/openapi.yaml post /blockchain_link/blockchains/{blockchainId}/activate Starts the asynchronous activation workflow for the blockchain identified by its ID, transitioning it towards the ACTIVATED state. # Create a new blockchain Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/create-a-new-blockchain https://swagger.fireblocks.com/openapi.yaml post /blockchain_link/blockchains Registers a new tenant-managed blockchain from the supplied declared properties. The blockchain starts in the CREATED state and must be activated separately before it can be used. # Delete a blockchain Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/delete-a-blockchain https://swagger.fireblocks.com/openapi.yaml delete /blockchain_link/blockchains/{blockchainId} Permanently removes a blockchain identified by its ID. The blockchain must not be in an active lifecycle state. # Get a blockchain by ID Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/get-a-blockchain-by-id https://swagger.fireblocks.com/openapi.yaml get /blockchain_link/blockchains/{blockchainId} Returns a single blockchain owned by the tenant, identified by its ID. # Get tenant billing info Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/get-tenant-billing-info https://swagger.fireblocks.com/openapi.yaml get /blockchain_link/blockchains/billing_info Returns the tenant's blockchain activation limit and current usage. tenant_id is derived from the JWT token context. # Get the test wallet address Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/get-the-test-wallet-address https://swagger.fireblocks.com/openapi.yaml get /blockchain_link/blockchains/test_wallet_address Returns the Ethereum address derived from the configured external wallet private key, used by the UI for test transfers. tenant_id is derived from the JWT token context. # List blockchains with pagination and filtering Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/list-blockchains-with-pagination-and-filtering https://swagger.fireblocks.com/openapi.yaml get /blockchain_link/blockchains Returns the tenant's blockchains, paginated and filterable by state, network environment, and free-text search, with configurable sorting. # Trigger validation workflow Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/trigger-validation-workflow https://swagger.fireblocks.com/openapi.yaml post /blockchain_link/blockchains/{blockchainId}/validate Starts the asynchronous validation workflow for a blockchain and its associated validation session. # Update a blockchain Source: https://developers.fireblocks.com/api-reference/blockchain-link-beta/update-a-blockchain https://swagger.fireblocks.com/openapi.yaml put /blockchain_link/blockchains/{blockchainId} Updates the declared properties of an existing blockchain identified by its ID. Only the fields supplied in the request are modified. # Get a Blockchain by ID Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/get-a-blockchain-by-id https://swagger.fireblocks.com/openapi.yaml get /blockchains/{id} Returns a blockchain by ID or legacyID. # Get an asset Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/get-an-asset https://swagger.fireblocks.com/openapi.yaml get /assets/{id} Returns an asset by ID or legacyID. **Note**: - We will continue displaying and supporting the legacy ID (API ID). Since not all Fireblocks services fully support the new Assets UUID, please use only the legacy ID until further notice. # List assets Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/list-assets https://swagger.fireblocks.com/openapi.yaml get /assets Retrieves a paginated list of all assets supported by Fireblocks in your workspace **Note:** We will continue to support and display the legacy ID (API ID). Since not all Fireblocks services fully support the new Assets UUID, please use only the legacy ID until further notice. # List assets (Legacy) Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/list-assets-legacy https://swagger.fireblocks.com/openapi.yaml get /supported_assets **This legacy endpoint has not been deprecated but it should not be used in your operations. Instead, use the new [List assets](https://developers.fireblocks.com/reference/listassets) endpoint for better performance and to retrieve more detailed asset information.** Retrieves all assets supported by Fireblocks in your workspace. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor. # List blockchains Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/list-blockchains https://swagger.fireblocks.com/openapi.yaml get /blockchains Returns all blockchains supported by Fireblocks. # Register an asset Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/register-an-asset https://swagger.fireblocks.com/openapi.yaml post /assets Register a new asset to a workspace and return the newly created asset's details. Currently supported chains are: - EVM based chains - Stellar - Algorand - TRON - NEAR - Solana - Sui - TON # Set asset price Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/set-asset-price https://swagger.fireblocks.com/openapi.yaml post /assets/prices/{id} Set asset price for the given asset id. Returns the asset price response. # Update the user’s metadata for an asset Source: https://developers.fireblocks.com/api-reference/blockchains-&-assets/update-the-user’s-metadata-for-an-asset https://swagger.fireblocks.com/openapi.yaml patch /assets/{id} Update the user’s metadata for an asset. Endpoint Permission: Owner, Admin, Non-Signing Admin, NCW Admin, Signer, Editor. # Answer an offer Source: https://developers.fireblocks.com/api-reference/canton-beta/answer-an-offer https://swagger.fireblocks.com/openapi.yaml post /operations/canton/offers/{offerId}/responses Responds to an incoming Canton offer. Set `responseType` to one of the values in the offer transaction's `cantonDetails.offerResponse.availableResponses`. # Make a Canton call Source: https://developers.fireblocks.com/api-reference/canton-beta/make-a-canton-call https://swagger.fireblocks.com/openapi.yaml post /operations/canton/calls Submits a Canton call from a vault account's Canton wallet. `type` selects the call and the shape of `payload`. # Get a Compliance Orchestrator screening's result Source: https://developers.fireblocks.com/api-reference/compliance-orchestrator-beta/get-a-compliance-orchestrator-screenings-result https://swagger.fireblocks.com/openapi.yaml get /compliance/orchestrator/screenings/{screeningId} Returns the result of a screening started by `POST /v1/compliance/orchestrator/screenings`, with a result per workflow step and an audit log. Safe to poll: a screening still in flight reports `PENDING` or `RUNNING`. Only screenings owned by the requesting tenant are returned. # Get a Compliance Orchestrator workflow Source: https://developers.fireblocks.com/api-reference/compliance-orchestrator-beta/get-a-compliance-orchestrator-workflow https://swagger.fireblocks.com/openapi.yaml get /compliance/orchestrator/workflows/{workflowId} Returns a workflow's status and its steps in execution order. Read it to see what a given `workflowId` will screen, and which fields its rules may reference. # Trigger a Compliance Orchestrator screening Source: https://developers.fireblocks.com/api-reference/compliance-orchestrator-beta/trigger-a-compliance-orchestrator-screening https://swagger.fireblocks.com/openapi.yaml post /compliance/orchestrator/screenings Starts a compliance screening against an active workflow and returns a `screeningId`. The screening runs asynchronously — poll `GET /v1/compliance/orchestrator/screenings/{screeningId}` for the result. Unlike the screening that applies automatically to submitted transactions under `/v1/screening`, this is called on demand, before anything exists on-chain. # Update a Compliance Orchestrator workflow's status Source: https://developers.fireblocks.com/api-reference/compliance-orchestrator-beta/update-a-compliance-orchestrator-workflows-status https://swagger.fireblocks.com/openapi.yaml patch /compliance/orchestrator/workflows/{workflowId}/status Moves a workflow between `DRAFT` and `ACTIVE`. A workflow must be `ACTIVE` before `POST /v1/compliance/orchestrator/screenings` will accept a screening against it. Returns the workflow's id and new status, not its full configuration. # Get AML Screening Policy Configuration Source: https://developers.fireblocks.com/api-reference/compliance-screening-configuration/get-aml-screening-policy-configuration https://swagger.fireblocks.com/openapi.yaml get /screening/aml/policy_configuration Retrieves the configuration for Travel Rule screening policy. # Get Travel Rule Screening Policy Configuration Source: https://developers.fireblocks.com/api-reference/compliance-screening-configuration/get-travel-rule-screening-policy-configuration https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/policy_configuration Retrieves the configuration for Travel Rule screening policy. # Activate ARS (Address Registry Screening) Source: https://developers.fireblocks.com/api-reference/compliance/activate-ars-address-registry-screening https://swagger.fireblocks.com/openapi.yaml post /screening/ars/config/activate Activates ARS (Address Registry Screening) for the authenticated tenant (sets config.active to true). Once activated, ARS screening applies to matching transactions. # Activate BYORK Light Source: https://developers.fireblocks.com/api-reference/compliance/activate-byork-light https://swagger.fireblocks.com/openapi.yaml post /screening/byork/config/activate Activates BYORK Light for the authenticated tenant (sets config.active to true). Once activated, BYORK screening applies to matching transactions. Requires BYORK Light to be enabled for the tenant (contact your CSM to enable). # Add vault accounts to the address registry opt-out list Source: https://developers.fireblocks.com/api-reference/compliance/add-vault-accounts-to-the-address-registry-opt-out-list https://swagger.fireblocks.com/openapi.yaml post /address_registry/vaults Adds one or more vault account ids to the workspace opt-out list for the address registry. # AML - View Post-Screening Policy Source: https://developers.fireblocks.com/api-reference/compliance/aml--view-post-screening-policy https://swagger.fireblocks.com/openapi.yaml get /screening/aml/post_screening_policy Get the post-screening policy for AML. # AML - View Screening Policy Source: https://developers.fireblocks.com/api-reference/compliance/aml--view-screening-policy https://swagger.fireblocks.com/openapi.yaml get /screening/aml/screening_policy Get the screening policy for AML. # Assign vault accounts to a legal entity Source: https://developers.fireblocks.com/api-reference/compliance/assign-vault-accounts-to-a-legal-entity https://swagger.fireblocks.com/openapi.yaml post /legal_entities/{legalEntityId}/vaults Assigns one or more vault accounts to a specific legal entity registration. Explicitly mapped vault accounts take precedence over the workspace default legal entity. Endpoint Permission: Admin, Non-Signing Admin. # Bypass Screening Policy Source: https://developers.fireblocks.com/api-reference/compliance/bypass-screening-policy https://swagger.fireblocks.com/openapi.yaml post /screening/transaction/{txId}/bypass_screening_policy Triggers a new transaction, with the API user as the initiator, bypassing the screening policy checks. This endpoint is restricted to Admin API users and is only applicable to outgoing transactions. # Create a counterparty group Source: https://developers.fireblocks.com/api-reference/compliance/create-a-counterparty-group https://swagger.fireblocks.com/openapi.yaml post /counterparty_groups Creates a new counterparty group. **Endpoint Permissions:** Admin, Non-Signing Admin. # Create a Proof of Ownership PDF for an address Source: https://developers.fireblocks.com/api-reference/compliance/create-a-proof-of-ownership-pdf-for-an-address https://swagger.fireblocks.com/openapi.yaml post /address_registry/proof_of_ownership_exports Creates a Proof of Ownership PDF for a blockchain address owned by the authenticated workspace — for example, to share with a counterparty or bank as compliance evidence. Recipients can confirm it with `POST /v1/address_registry/proof_of_ownership_exports/verify`. Check `proofOfOwnershipAvailable` on `GET /v1/address_registry/legal_entities/{address}` first if you want to know whether create is likely to succeed. # Deactivate ARS (Address Registry Screening) Source: https://developers.fireblocks.com/api-reference/compliance/deactivate-ars-address-registry-screening https://swagger.fireblocks.com/openapi.yaml post /screening/ars/config/deactivate Deactivates ARS (Address Registry Screening) for the authenticated tenant (sets config.active to false). Once deactivated, ARS screening no longer applies until activated again. # Deactivate BYORK Light Source: https://developers.fireblocks.com/api-reference/compliance/deactivate-byork-light https://swagger.fireblocks.com/openapi.yaml post /screening/byork/config/deactivate Deactivates BYORK Light for the authenticated tenant (sets config.active to false). Once deactivated, BYORK screening no longer applies until activated again. Requires BYORK Light to be enabled for the tenant (contact your CSM to enable). # Delete a counterparty group Source: https://developers.fireblocks.com/api-reference/compliance/delete-a-counterparty-group https://swagger.fireblocks.com/openapi.yaml delete /counterparty_groups/{groupId} Permanently deletes a counterparty group. **Endpoint Permissions:** Admin, Non-Signing Admin. # Delete a legal entity Source: https://developers.fireblocks.com/api-reference/compliance/delete-a-legal-entity https://swagger.fireblocks.com/openapi.yaml delete /legal_entities/{legalEntityId} Delete a legal entity will change the status of a legal entity registration to REVOKED. Endpoint Permission: Admin, Non-Signing Admin. # Get a counterparty group Source: https://developers.fireblocks.com/api-reference/compliance/get-a-counterparty-group https://swagger.fireblocks.com/openapi.yaml get /counterparty_groups/{groupId} Returns the details of a specific counterparty group. **Endpoint Permissions:** Admin, Non-Signing Admin, Viewer. # Get a legal entity Source: https://developers.fireblocks.com/api-reference/compliance/get-a-legal-entity https://swagger.fireblocks.com/openapi.yaml get /legal_entities/{legalEntityId} Returns details of a specific legal entity registration, including GLEIF data when available. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get address registry status for this workspace Source: https://developers.fireblocks.com/api-reference/compliance/get-address-registry-status-for-this-workspace https://swagger.fireblocks.com/openapi.yaml get /address_registry/tenant Check whether this workspace uses the address registry. The response is `OPTED_IN` or `OPTED_OUT`. If this workspace has not been configured yet, it follows your customer's default (see `GET /v1/address_registry/customer`) when that default is `OPTED_IN` or `OPTED_OUT`; otherwise the workspace is opted in by default. # Get BYORK Light configuration Source: https://developers.fireblocks.com/api-reference/compliance/get-byork-light-configuration https://swagger.fireblocks.com/openapi.yaml get /screening/byork/config Retrieves BYORK Light configuration for the authenticated tenant (timeouts, active flag, allowed timeout ranges). Returns default config when none exists. Requires BYORK Light to be enabled for the tenant. # Get BYORK Light verdict Source: https://developers.fireblocks.com/api-reference/compliance/get-byork-light-verdict https://swagger.fireblocks.com/openapi.yaml get /screening/byork/verdict Returns the current BYORK verdict and status for a transaction. Status can be PRE_ACCEPTED, PENDING, RECEIVED (verdict is final but processing not yet complete), or COMPLETED. Requires BYORK Light to be enabled for the tenant. Returns 404 if no BYORK verdict is found for the transaction. # Get whether a vault account is opted out of the address registry Source: https://developers.fireblocks.com/api-reference/compliance/get-whether-a-vault-account-is-opted-out-of-the-address-registry https://swagger.fireblocks.com/openapi.yaml get /address_registry/vaults/{vaultAccountId} Returns whether this vault account is on the workspace opt-out list (`optedOut` true or false). List, add, and clear-all are available on `/v1/address_registry/vaults`; this path reads or removes one vault. # Get your customer's address registry default Source: https://developers.fireblocks.com/api-reference/compliance/get-your-customers-address-registry-default https://swagger.fireblocks.com/openapi.yaml get /address_registry/customer Read your customer's organization-wide address registry setting. Workspaces without their own choice use this as the default. Returns `NOT_SET` when no choice has been made yet, or `OPTED_IN` / `OPTED_OUT` when one has. # List counterparty groups Source: https://developers.fireblocks.com/api-reference/compliance/list-counterparty-groups https://swagger.fireblocks.com/openapi.yaml get /counterparty_groups Returns a paginated list of counterparty groups. **Endpoint Permissions:** Admin, Non-Signing Admin, Viewer. # List legal entities (Paginated) Source: https://developers.fireblocks.com/api-reference/compliance/list-legal-entities-paginated https://swagger.fireblocks.com/openapi.yaml get /legal_entities Returns legal entity registrations for the workspace with cursor-based pagination. If query parameter vaultAccountId is used it returns the legal entity registration associated with a specific vault account. If no explicit mapping exists for the vault, the workspace default legal entity is returned. Returns an empty response if neither a vault mapping nor a default legal entity is configured. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List vault accounts for a legal entity (Paginated) Source: https://developers.fireblocks.com/api-reference/compliance/list-vault-accounts-for-a-legal-entity-paginated https://swagger.fireblocks.com/openapi.yaml get /legal_entities/{legalEntityId}/vaults Returns vault account IDs explicitly assigned to a specific legal entity registration, with cursor-based pagination. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List vault-level address registry opt-outs (paginated) Source: https://developers.fireblocks.com/api-reference/compliance/list-vault-level-address-registry-opt-outs-paginated https://swagger.fireblocks.com/openapi.yaml get /address_registry/vaults Lists vault accounts that are opted out of the address registry for this workspace. Pagination uses `next` and `prev` cursors from the response. If `pageSize` is omitted, **50** items are returned per page; allowed range is **1–100** per request. # Look up legal entity by blockchain address Source: https://developers.fireblocks.com/api-reference/compliance/look-up-legal-entity-by-blockchain-address https://swagger.fireblocks.com/openapi.yaml get /address_registry/legal_entities/{address} Returns legal entity information for the given blockchain address (LEI data availability, LEI identifier, Travel Rule providers, contact email, and related fields — see response schema). URL-encode `{address}` when required. # Opt this workspace in to the address registry Source: https://developers.fireblocks.com/api-reference/compliance/opt-this-workspace-in-to-the-address-registry https://swagger.fireblocks.com/openapi.yaml post /address_registry/tenant Turn on the address registry for this workspace. No request body. Returns `OPTED_IN`. # Opt this workspace out of the address registry Source: https://developers.fireblocks.com/api-reference/compliance/opt-this-workspace-out-of-the-address-registry https://swagger.fireblocks.com/openapi.yaml delete /address_registry/tenant Turn off the address registry for this workspace. No request body. Returns `OPTED_OUT`. # Opt your customer in to the address registry Source: https://developers.fireblocks.com/api-reference/compliance/opt-your-customer-in-to-the-address-registry https://swagger.fireblocks.com/openapi.yaml post /address_registry/customer Set your customer's default to opted in. Workspaces without their own setting will follow this default. No request body. Returns `OPTED_IN`. If the customer for this workspace cannot be determined, returns HTTP 404 with error code **2147**. # Opt your customer out of the address registry Source: https://developers.fireblocks.com/api-reference/compliance/opt-your-customer-out-of-the-address-registry https://swagger.fireblocks.com/openapi.yaml delete /address_registry/customer Set your customer's default to opted out. Workspaces without their own setting will follow this default. No request body. Returns `OPTED_OUT`. If the customer for this workspace cannot be determined, returns HTTP 404 with error code **2147**. # Provides all the compliance details for the given screened transaction. Source: https://developers.fireblocks.com/api-reference/compliance/provides-all-the-compliance-details-for-the-given-screened-transaction https://swagger.fireblocks.com/openapi.yaml get /screening/transaction/{txId} Provides all the compliance details for the given screened transaction. # Register a new legal entity Source: https://developers.fireblocks.com/api-reference/compliance/register-a-new-legal-entity https://swagger.fireblocks.com/openapi.yaml post /legal_entities Registers a new legal entity for the workspace using its LEI (Legal Entity Identifier) code. The LEI is validated against the GLEIF registry. Each workspace can register multiple legal entities. Endpoint Permission: Admin, Non-Signing Admin. # Remove a single vault account from the address registry opt-out list Source: https://developers.fireblocks.com/api-reference/compliance/remove-a-single-vault-account-from-the-address-registry-opt-out-list https://swagger.fireblocks.com/openapi.yaml delete /address_registry/vaults/{vaultAccountId} Removes this vault account id from the workspace opt-out list if it is present; otherwise the call still succeeds. Response body matches GET (`optedOut` is `false` after success). To clear the whole list, use `DELETE /v1/address_registry/vaults`. # Remove all vault-level address registry opt-outs for the workspace Source: https://developers.fireblocks.com/api-reference/compliance/remove-all-vault-level-address-registry-opt-outs-for-the-workspace https://swagger.fireblocks.com/openapi.yaml delete /address_registry/vaults Removes all vault accounts from the workspace opt-out list. # Rescreen a rejected transaction Source: https://developers.fireblocks.com/api-reference/compliance/rescreen-a-rejected-transaction https://swagger.fireblocks.com/openapi.yaml post /screening/transaction/{txId}/rescreen Re-runs compliance screening on an incoming transaction that was rejected or failed by screening checks, moving it back to pending screening. This endpoint is only applicable to incoming transactions with a rejected/failed AML screening status. # Set AML Verdict (BYORK Super Light) Source: https://developers.fireblocks.com/api-reference/compliance/set-aml-verdict-byork-super-light https://swagger.fireblocks.com/openapi.yaml post /screening/aml/verdict/manual Set AML verdict for incoming transactions when **BYORK Super Light** (Manual Screening Verdict) is enabled. This endpoint is for Super Light only. For **BYORK Light**, use POST /screening/byork/verdict instead. When Super Light is retired, this endpoint will be deprecated; use the BYORK Light verdict API for new integrations. # Set BYORK Light timeouts Source: https://developers.fireblocks.com/api-reference/compliance/set-byork-light-timeouts https://swagger.fireblocks.com/openapi.yaml put /screening/byork/config/timeouts Updates timeout values for BYORK wait-for-response (incoming and/or outgoing). At least one of incomingTimeoutSeconds or outgoingTimeoutSeconds is required. Values must be within the ranges returned in GET config (timeoutRangeIncoming for incomingTimeoutSeconds, timeoutRangeOutgoing for outgoingTimeoutSeconds). Requires BYORK Light to be enabled for the tenant (contact your CSM to enable). # Set BYORK Light verdict Source: https://developers.fireblocks.com/api-reference/compliance/set-byork-light-verdict https://swagger.fireblocks.com/openapi.yaml post /screening/byork/verdict Submit verdict (ACCEPT or REJECT) for a transaction in the BYORK Light flow. If the transaction is awaiting your decision, the verdict is applied immediately (response status COMPLETED). If processing has not yet reached that point, the verdict is stored and applied when it does (response status PRE_ACCEPTED). Requires BYORK Light to be enabled for the tenant. # Tenant - Screening Configuration Source: https://developers.fireblocks.com/api-reference/compliance/tenant--screening-configuration https://swagger.fireblocks.com/openapi.yaml put /screening/configurations Update tenant screening configuration. # Travel Rule - View Post-Screening Policy Source: https://developers.fireblocks.com/api-reference/compliance/travel-rule--view-post-screening-policy https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/post_screening_policy Get the post-screening policy for Travel Rule. # Travel Rule - View Screening Policy Source: https://developers.fireblocks.com/api-reference/compliance/travel-rule--view-screening-policy https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/screening_policy Get the screening policy for Travel Rule. # Update a counterparty group Source: https://developers.fireblocks.com/api-reference/compliance/update-a-counterparty-group https://swagger.fireblocks.com/openapi.yaml patch /counterparty_groups/{groupId} Updates an existing counterparty group. **Endpoint Permissions:** Admin, Non-Signing Admin. # Update AML Configuration Source: https://developers.fireblocks.com/api-reference/compliance/update-aml-configuration https://swagger.fireblocks.com/openapi.yaml put /screening/aml/policy_configuration Updates bypass screening, inbound delay, or outbound delay configurations for AML. # Update legal entity Source: https://developers.fireblocks.com/api-reference/compliance/update-legal-entity https://swagger.fireblocks.com/openapi.yaml put /legal_entities/{legalEntityId} Updates the status of a legal entity registration. Setting isDefault to true marks the registration as the workspace default, which is applied to vault accounts that have no explicit legal entity mapping. Endpoint Permission: Admin, Non-Signing Admin. # Update Travel Rule Configuration Source: https://developers.fireblocks.com/api-reference/compliance/update-travel-rule-configuration https://swagger.fireblocks.com/openapi.yaml put /screening/travel_rule/policy_configuration Updates bypass screening, inbound delay, or outbound delay configurations for Travel Rule. # Verify a Proof of Ownership export Source: https://developers.fireblocks.com/api-reference/compliance/verify-a-proof-of-ownership-export https://swagger.fireblocks.com/openapi.yaml post /address_registry/proof_of_ownership_exports/verify Verifies a Proof of Ownership export against the record Fireblocks stored at creation. Returns `valid: false` (not 404) for an unknown, expired, or mismatched export. Available to any authenticated Fireblocks workspace, not just the export's original owner. # Add a connected account Source: https://developers.fireblocks.com/api-reference/connected-accounts/add-a-connected-account https://swagger.fireblocks.com/openapi.yaml post /connected_accounts Creates a new connected account for the authenticated tenant. The `creds` field must be a Base64-encoded RSA-encrypted credential blob. Use `GET /connected_accounts/credentials/public_key` to retrieve the public key for encryption. The `providerType` is derived server-side from the `providerId` — callers do not supply it. Endpoint Permission: Editor, Admin, Non-Signing Admin. # Disconnect connected account Source: https://developers.fireblocks.com/api-reference/connected-accounts/disconnect-connected-account https://swagger.fireblocks.com/openapi.yaml delete /connected_accounts/{accountId} Disconnect a connected account by ID. # Get a single allowlist entry for a connected account Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-a-single-allowlist-entry-for-a-connected-account https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/{accountId}/allowlist/{allowlistId} Retrieves a single allowlist entry by its Fireblocks identifier for a specified connected account. **Note:** Currently supports CoinbaseExchange/Binance accounts only. # Get allowlist for connected account Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-allowlist-for-connected-account https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/{accountId}/allowlist Retrieves the address allowlist for a specified connected account. **Note:** Currently supports CoinbaseExchange/Binance accounts only. # Get balances for an account Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-balances-for-an-account https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/{accountId}/balances Retrieve current asset balances for a specific connected account as a flat list (one row per `assetId`, `balanceType`). # Get connected account Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-connected-account https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/{accountId} Retrieve detailed information about a specific connected account by ID. # Get connected accounts Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-connected-accounts https://swagger.fireblocks.com/openapi.yaml get /connected_accounts Returns all connected accounts. # Get exchange rates for an account Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-exchange-rates-for-an-account https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/{accountId}/rates Retrieve current exchange rates for converting between specific assets in a connected account. # Get public key to encrypt connected account credentials Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-public-key-to-encrypt-connected-account-credentials https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/credentials/public_key Returns the RSA public key used to encrypt the `creds` field before calling `POST /connected_accounts`. The key is a singleton resource scoped to the connected-accounts credentials domain — there is one per tenant context. # Get supported trading pairs for an account Source: https://developers.fireblocks.com/api-reference/connected-accounts/get-supported-trading-pairs-for-an-account https://swagger.fireblocks.com/openapi.yaml get /connected_accounts/{accountId}/manifest/capabilities/trading/pairs Retrieve all asset trading pairs supported by a specific connected account, including the pair type (`quote`, `market`, `onOffRamp`). # Rename Connected Account Source: https://developers.fireblocks.com/api-reference/connected-accounts/rename-connected-account https://swagger.fireblocks.com/openapi.yaml post /connected_accounts/{accountId}/rename Rename a connected account by account ID. # Sync allowlist for connected account Source: https://developers.fireblocks.com/api-reference/connected-accounts/sync-allowlist-for-connected-account https://swagger.fireblocks.com/openapi.yaml post /connected_accounts/{accountId}/allowlist/sync Triggers an on-demand sync from the exchange, bypassing the cache and fetching live data immediately. **Rate limit:** 1 request per minute per connected account. **Note:** Currently supports CoinbaseExchange/Binance accounts only. # Update connected account credentials Source: https://developers.fireblocks.com/api-reference/connected-accounts/update-connected-account-credentials https://swagger.fireblocks.com/openapi.yaml post /connected_accounts/{accountId}/credentials Replace the API credentials (secret + API key) of a connected account. Credentials belong to an API key, which can back a single account or an entire hierarchy. Updating them affects all accounts sharing that key, so the endpoint returns an array of modified accounts. The `creds` field must be a Base64-encoded RSA-encrypted credential blob; use `GET /connected_accounts/credentials/public_key` to retrieve the public key for encryption. Both `creds` and `apiKey` are mandatory. Validation against the exchange is synchronous, but the update itself is **pending mobile approval** — the existing credentials stay live until the change is approved, so none of the affected accounts are disconnected in the meantime. Endpoint Permission: Admin, Non-Signing Admin. # Create console user Source: https://developers.fireblocks.com/api-reference/console-user/create-console-user https://swagger.fireblocks.com/openapi.yaml post /management/users Create console users in your workspace - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Learn more about Fireblocks Users management in the following [guide](https://developers.fireblocks.com/docs/manage-users). Endpoint Permission: Admin, Non-Signing Admin. # Get console users Source: https://developers.fireblocks.com/api-reference/console-user/get-console-users https://swagger.fireblocks.com/openapi.yaml get /management/users Get console users for your workspace. - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Endpoint Permission: Admin, Non-Signing Admin. # Request deletion of a console user Source: https://developers.fireblocks.com/api-reference/console-user/request-deletion-of-a-console-user https://swagger.fireblocks.com/openapi.yaml delete /management/users/{id} Requests deletion of a console user. The request is asynchronous: it goes through the workspace's configured "Delete users" approval policy (Settings > Quorums), exactly as deleting a user from the console does, and the user is removed only once that approval completes. - The workspace must have a "Delete users" approval policy (admin quorum or approval group). If "Delete users" is Owner-only, which is the default, the request is refused with OWNER_ONLY (2760) and the Owner deletes users in the Console. With an admin quorum, the requesting API user's own approval counts toward the quorum. - Track progress by polling GET /management/users; deletion is complete when the user is disabled. - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin/Security Admin permissions. Endpoint Permission: Admin, Non-Signing Admin, Security Admin. **Note:** This endpoint is currently in beta and might be subject to changes. # List contacts Source: https://developers.fireblocks.com/api-reference/contacts/list-contacts https://swagger.fireblocks.com/openapi.yaml get /contacts Returns a paginated list of the workspace's address book contacts. Live contacts are returned by default; pass `archived=true` to return only the archived ones. Results are sorted by `name` ascending unless `sortBy`/`order` say otherwise. Because the sort column is the page cursor's leading key, a `pageCursor` must be replayed with the same sort it was minted under, or the request is rejected. Endpoint Permissions: any workspace role may read the address book. Writes are role-gated. # Call a read function on a deployed contract Source: https://developers.fireblocks.com/api-reference/contract-interactions/call-a-read-function-on-a-deployed-contract https://swagger.fireblocks.com/openapi.yaml post /contract_interactions/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/functions/read Call a read function on a deployed contract by blockchain native asset id and contract address # Call a write function on a deployed contract Source: https://developers.fireblocks.com/api-reference/contract-interactions/call-a-write-function-on-a-deployed-contract https://swagger.fireblocks.com/openapi.yaml post /contract_interactions/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/functions/write Call a write function on a deployed contract by blockchain native asset id and contract address. This creates an onchain transaction, thus it is an async operation. It returns a transaction id that can be polled for status check # Decode a function call data, error, or event log Source: https://developers.fireblocks.com/api-reference/contract-interactions/decode-a-function-call-data-error-or-event-log https://swagger.fireblocks.com/openapi.yaml post /contract_interactions/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/decode Decode a function call data, error, or event log from a deployed contract by blockchain native asset id and contract address. # Get contract address by transaction hash Source: https://developers.fireblocks.com/api-reference/contract-interactions/get-contract-address-by-transaction-hash https://swagger.fireblocks.com/openapi.yaml get /contract_interactions/base_asset_id/{baseAssetId}/tx_hash/{txHash} Retrieve the contract address by blockchain native asset ID and transaction hash # Get transaction receipt Source: https://developers.fireblocks.com/api-reference/contract-interactions/get-transaction-receipt https://swagger.fireblocks.com/openapi.yaml get /contract_interactions/base_asset_id/{baseAssetId}/tx_hash/{txHash}/receipt Retrieve the transaction receipt by blockchain native asset ID and transaction hash > **Note** > This functionality is exclusively available for EVM (Ethereum Virtual Machine) compatible chains. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, and Viewer. # Return deployed contract's ABI Source: https://developers.fireblocks.com/api-reference/contract-interactions/return-deployed-contracts-abi https://swagger.fireblocks.com/openapi.yaml get /contract_interactions/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/functions Return deployed contract's ABI by blockchain native asset id and contract address. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, and Viewer. # Delete a contract template by id Source: https://developers.fireblocks.com/api-reference/contract-templates/delete-a-contract-template-by-id https://swagger.fireblocks.com/openapi.yaml delete /tokenization/templates/{contractTemplateId} Delete a contract by id. allowed only for private contract templates. Notice: it is irreversible! # Deploy contract Source: https://developers.fireblocks.com/api-reference/contract-templates/deploy-contract https://swagger.fireblocks.com/openapi.yaml post /tokenization/templates/{contractTemplateId}/deploy Deploy a new contract by contract template id. If you wish to deploy a token (ERC20, ERC721 etc), and create asset please use POST /tokenization # Get supported blockchains for the template Source: https://developers.fireblocks.com/api-reference/contract-templates/get-supported-blockchains-for-the-template https://swagger.fireblocks.com/openapi.yaml get /tokenization/templates/{contractTemplateId}/supported_blockchains Get supported blockchains for the template # List all contract templates Source: https://developers.fireblocks.com/api-reference/contract-templates/list-all-contract-templates https://swagger.fireblocks.com/openapi.yaml get /tokenization/templates Return minimal representation of all the contract templates available for the workspace. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Return contract template by id Source: https://developers.fireblocks.com/api-reference/contract-templates/return-contract-template-by-id https://swagger.fireblocks.com/openapi.yaml get /tokenization/templates/{contractTemplateId} Return detailed information about the contract template # Return contract template's constructor Source: https://developers.fireblocks.com/api-reference/contract-templates/return-contract-templates-constructor https://swagger.fireblocks.com/openapi.yaml get /tokenization/templates/{contractTemplateId}/constructor Return contract template's constructor ABI # Return contract template's function Source: https://developers.fireblocks.com/api-reference/contract-templates/return-contract-templates-function https://swagger.fireblocks.com/openapi.yaml get /tokenization/templates/{contractTemplateId}/function Return contract template`s function ABI by signature # Upload contract template Source: https://developers.fireblocks.com/api-reference/contract-templates/upload-contract-template https://swagger.fireblocks.com/openapi.yaml post /tokenization/templates Upload a new contract template. This contract template will be available for the workspace # Add a contract Source: https://developers.fireblocks.com/api-reference/contracts/add-a-contract https://swagger.fireblocks.com/openapi.yaml post /contracts Adds a contract to the workspace whitelist. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Add an asset to a whitelisted contract Source: https://developers.fireblocks.com/api-reference/contracts/add-an-asset-to-a-whitelisted-contract https://swagger.fireblocks.com/openapi.yaml post /contracts/{contractId}/{assetId} Adds an asset to a whitelisted contract. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Delete a contract Source: https://developers.fireblocks.com/api-reference/contracts/delete-a-contract https://swagger.fireblocks.com/openapi.yaml delete /contracts/{contractId} Deletes a contract by ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Delete an asset from a whitelisted contract Source: https://developers.fireblocks.com/api-reference/contracts/delete-an-asset-from-a-whitelisted-contract https://swagger.fireblocks.com/openapi.yaml delete /contracts/{contractId}/{assetId} Deletes a whitelisted contract asset by ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Find a Specific Whitelisted Contract Source: https://developers.fireblocks.com/api-reference/contracts/find-a-specific-whitelisted-contract https://swagger.fireblocks.com/openapi.yaml get /contracts/{contractId} Returns a whitelisted contract by Fireblocks Contract ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Find a whitelisted contract's asset Source: https://developers.fireblocks.com/api-reference/contracts/find-a-whitelisted-contracts-asset https://swagger.fireblocks.com/openapi.yaml get /contracts/{contractId}/{assetId} Returns a whitelisted contract's asset by ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # List Whitelisted Contracts Source: https://developers.fireblocks.com/api-reference/contracts/list-whitelisted-contracts https://swagger.fireblocks.com/openapi.yaml get /contracts Gets a list of whitelisted contracts. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Add cosigner Source: https://developers.fireblocks.com/api-reference/cosigners-beta/add-cosigner https://swagger.fireblocks.com/openapi.yaml post /cosigners Add a new cosigner. The cosigner will be pending pairing until the API key is manually paired Endpoint Permission: Admin and Non-Signing Admin. # Get all API keys Source: https://developers.fireblocks.com/api-reference/cosigners-beta/get-all-api-keys https://swagger.fireblocks.com/openapi.yaml get /cosigners/{cosignerId}/api_keys Get all cosigner paired API keys (paginated). **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin and Non-Signing Admin. # Get all cosigners Source: https://developers.fireblocks.com/api-reference/cosigners-beta/get-all-cosigners https://swagger.fireblocks.com/openapi.yaml get /cosigners Get all workspace cosigners (paginated). **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin and Non-Signing Admin. # Get API key Source: https://developers.fireblocks.com/api-reference/cosigners-beta/get-api-key https://swagger.fireblocks.com/openapi.yaml get /cosigners/{cosignerId}/api_keys/{apiKeyId} Get an API key by ID. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin and Non-Signing Admin. # Get cosigner Source: https://developers.fireblocks.com/api-reference/cosigners-beta/get-cosigner https://swagger.fireblocks.com/openapi.yaml get /cosigners/{cosignerId} Get a cosigner by ID. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin and Non-Signing Admin. # Get request status Source: https://developers.fireblocks.com/api-reference/cosigners-beta/get-request-status https://swagger.fireblocks.com/openapi.yaml get /cosigners/{cosignerId}/api_keys/{apiKeyId}/{requestId} Get the status of an asynchronous request Endpoint Permission: Admin and Non-Signing Admin. # Pair API key Source: https://developers.fireblocks.com/api-reference/cosigners-beta/pair-api-key https://swagger.fireblocks.com/openapi.yaml put /cosigners/{cosignerId}/api_keys/{apiKeyId} Pair an API key to a cosigner Endpoint Permission: Admin and Non-Signing Admin. # Rename cosigner Source: https://developers.fireblocks.com/api-reference/cosigners-beta/rename-cosigner https://swagger.fireblocks.com/openapi.yaml patch /cosigners/{cosignerId} Rename a cosigner by ID. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin and Non-Signing Admin. # Unpair API key Source: https://developers.fireblocks.com/api-reference/cosigners-beta/unpair-api-key https://swagger.fireblocks.com/openapi.yaml delete /cosigners/{cosignerId}/api_keys/{apiKeyId} Unpair an API key from a cosigner Endpoint Permission: Admin and Non-Signing Admin. # Update API key callback handler Source: https://developers.fireblocks.com/api-reference/cosigners-beta/update-api-key-callback-handler https://swagger.fireblocks.com/openapi.yaml patch /cosigners/{cosignerId}/api_keys/{apiKeyId} Update the callback handler of an API key Endpoint Permission: Admin and Non-Signing Admin. # Fetch the contract ABI Source: https://developers.fireblocks.com/api-reference/deployed-contracts/fetch-the-contract-abi https://swagger.fireblocks.com/openapi.yaml post /tokenization/contracts/fetch_abi Fetch the ABI. If not found fetch the ABI from the block explorer # List deployed contracts data Source: https://developers.fireblocks.com/api-reference/deployed-contracts/list-deployed-contracts-data https://swagger.fireblocks.com/openapi.yaml get /tokenization/contracts Return a filtered lean representation of the deployed contracts data on all blockchains (paginated) # Return deployed contract data Source: https://developers.fireblocks.com/api-reference/deployed-contracts/return-deployed-contract-data https://swagger.fireblocks.com/openapi.yaml get /tokenization/contracts/{assetId}/{contractAddress} Return deployed contract data by blockchain native asset id and contract address # Return deployed contract data by id Source: https://developers.fireblocks.com/api-reference/deployed-contracts/return-deployed-contract-data-by-id https://swagger.fireblocks.com/openapi.yaml get /tokenization/contracts/{id} Return deployed contract data by id # Save contract ABI Source: https://developers.fireblocks.com/api-reference/deployed-contracts/save-contract-abi https://swagger.fireblocks.com/openapi.yaml post /tokenization/contracts/abi Save contract ABI for the tenant # Approve earn provider terms of service Source: https://developers.fireblocks.com/api-reference/earn/approve-earn-provider-terms-of-service https://swagger.fireblocks.com/openapi.yaml post /earn/providers/approve_terms_of_service Approves earn provider terms of service for this workspace (one-time per tenant). When `isTermsApprovalRequired` is true on a provider (see list providers), call this once before creating or executing earn actions with providers that require it. After success, `GET /earn/providers` reflects `isTermsOfServiceApproved`. # Create and execute a lending action (deposit or withdraw) Source: https://developers.fireblocks.com/api-reference/earn/create-and-execute-a-lending-action-deposit-or-withdraw https://swagger.fireblocks.com/openapi.yaml post /earn/actions Creates and runs a sequence of on-chain steps for either a deposit into or a withdrawal from an earn vault/market. Specify the operation with `action` in the request body (`DEPOSIT` or `WITHDRAW`). # Get a single earn lending action Source: https://developers.fireblocks.com/api-reference/earn/get-a-single-earn-lending-action https://swagger.fireblocks.com/openapi.yaml get /earn/actions/{id} Returns one lending action by its action sequence id (tenant-scoped). # Get list of earn opportunities Source: https://developers.fireblocks.com/api-reference/earn/get-list-of-earn-opportunities https://swagger.fireblocks.com/openapi.yaml get /earn/opportunities Get list of earn opportunities (vaults). # Get list of earn positions Source: https://developers.fireblocks.com/api-reference/earn/get-list-of-earn-positions https://swagger.fireblocks.com/openapi.yaml get /earn/positions Get list of earn positions for accounts tracked for this workspace. Optional query parameters filter by chain, provider, and pagination. # Get list of earn providers Source: https://developers.fireblocks.com/api-reference/earn/get-list-of-earn-providers https://swagger.fireblocks.com/openapi.yaml get /earn/providers Get list of earn providers. # List earn lending actions Source: https://developers.fireblocks.com/api-reference/earn/list-earn-lending-actions https://swagger.fireblocks.com/openapi.yaml get /earn/actions Returns a paginated list of lending actions (deposits and withdrawals) for the authenticated tenant. # Add asset to account Source: https://developers.fireblocks.com/api-reference/embedded-wallets/add-asset-to-account https://swagger.fireblocks.com/openapi.yaml post /ncw/wallets/{walletId}/accounts/{accountId}/assets/{assetId} Get the addresses of a specific asset, under a specific account, under a specific Non Custodial Wallet # Assign a wallet Source: https://developers.fireblocks.com/api-reference/embedded-wallets/assign-a-wallet https://swagger.fireblocks.com/openapi.yaml post /ncw/wallets/{walletId}/assign Assign a specific Non Custodial Wallet to a user # Create a new account Source: https://developers.fireblocks.com/api-reference/embedded-wallets/create-a-new-account https://swagger.fireblocks.com/openapi.yaml post /ncw/wallets/{walletId}/accounts Create a new account under a specific Non Custodial Wallet # Create a new wallet Source: https://developers.fireblocks.com/api-reference/embedded-wallets/create-a-new-wallet https://swagger.fireblocks.com/openapi.yaml post /ncw/wallets Create new Non Custodial Wallet # Get a account Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-a-account https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/accounts/{accountId} Get a specific account under a specific Non Custodial Wallet # Get a wallet Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-a-wallet https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId} Get a wallet # Get device key setup state Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-device-key-setup-state https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/devices/{deviceId}/setup_status Get the state of the specific device setup key under a specific Non Custodial Wallet # Get Embedded Wallet Device Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-embedded-wallet-device https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/devices/{deviceId} Get specific device for a specific s Wallet # Get registered devices - paginated Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-registered-devices--paginated https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/devices_paginated Get a paginated list of registered devices for a specific Non Custodial Wallet # Get the public key for a derivation path Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-the-public-key-for-a-derivation-path https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/public_key_info Gets the public key information based on derivation path and signing algorithm within a Non-Custodial Wallet # Get the public key of an asset Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-the-public-key-of-an-asset https://swagger.fireblocks.com/openapi.yaml get /ncw/{walletId}/accounts/{accountId}/{assetId}/{change}/{addressIndex}/public_key_info Gets the public key of an asset associated with a specific account within a Non-Custodial Wallet # Get the public key of an asset Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-the-public-key-of-an-asset-1 https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/accounts/{accountId}/assets/{assetId}/{change}/{addressIndex}/public_key_info Gets the public key of an asset associated with a specific account within a Non-Custodial Wallet # Get wallet key setup state Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-wallet-key-setup-state https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/setup_status Get the key setup state for a specific Non Custodial Wallet, including required algorithms and device setup status # Get wallet Latest Backup details Source: https://developers.fireblocks.com/api-reference/embedded-wallets/get-wallet-latest-backup-details https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/backup/latest Get wallet Latest Backup details, including the deviceId, and backup time # List wallets Source: https://developers.fireblocks.com/api-reference/embedded-wallets/list-wallets https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets Get all Non Custodial Wallets # Refresh asset balance Source: https://developers.fireblocks.com/api-reference/embedded-wallets/refresh-asset-balance https://swagger.fireblocks.com/openapi.yaml put /ncw/wallets/{walletId}/accounts/{accountId}/assets/{assetId}/balance Refresh the balance of an asset in a specific account # Retrieve asset Source: https://developers.fireblocks.com/api-reference/embedded-wallets/retrieve-asset https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/accounts/{accountId}/assets/{assetId} Get asset under a specific account, under a specific Non Custodial Wallet # Retrieve asset addresses Source: https://developers.fireblocks.com/api-reference/embedded-wallets/retrieve-asset-addresses https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/accounts/{accountId}/assets/{assetId}/addresses Get the addresses of a specific asset, under a specific account, under a specific Non Custodial Wallet # Retrieve asset balance Source: https://developers.fireblocks.com/api-reference/embedded-wallets/retrieve-asset-balance https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/accounts/{accountId}/assets/{assetId}/balance Get balance for specific asset, under a specific account # Retrieve assets Source: https://developers.fireblocks.com/api-reference/embedded-wallets/retrieve-assets https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/{walletId}/accounts/{accountId}/assets Retrieve assets for a specific account under a specific Non Custodial Wallet # Retrieve supported assets Source: https://developers.fireblocks.com/api-reference/embedded-wallets/retrieve-supported-assets https://swagger.fireblocks.com/openapi.yaml get /ncw/wallets/supported_assets Get all the available supported assets for the Non-Custodial Wallet # Update device status Source: https://developers.fireblocks.com/api-reference/embedded-wallets/update-device-status https://swagger.fireblocks.com/openapi.yaml patch /ncw/wallets/{walletId}/devices/{deviceId}/status Update the enabled/disabled status of a specific device for a Non Custodial Wallet # Update wallet status Source: https://developers.fireblocks.com/api-reference/embedded-wallets/update-wallet-status https://swagger.fireblocks.com/openapi.yaml patch /ncw/wallets/{walletId}/status Update the enabled/disabled status of a specific Non Custodial Wallet # Add an exchange account Source: https://developers.fireblocks.com/api-reference/exchange-accounts/add-an-exchange-account https://swagger.fireblocks.com/openapi.yaml post /exchange_accounts Add an exchange account to exchanges. Note: This endpoint currently only supports the following exchanges `INDEPENDENT_RESERVE`,`BIT`, `BITHUMB`, `BITSO`, `CRYPTOCOM`, `BYBIT_V2`, `WHITEBIT`, `HITBTC`, `GEMINI`, `HUOBI`, `GATEIO`, `COINHAKO`, `BULLISH`, `BITGET`, and `LUNO` To add an exchange account, please use the following [guide](https://developers.fireblocks.com/docs/add-an-exchange-account). # Convert exchange account funds Source: https://developers.fireblocks.com/api-reference/exchange-accounts/convert-exchange-account-funds https://swagger.fireblocks.com/openapi.yaml post /exchange_accounts/{exchangeAccountId}/convert Convert exchange account funds from the source asset to the destination asset. Coinbase (USD to USDC, USDC to USD) and Bitso (MXN to USD) are supported conversions. Learn more about Fireblocks Exchange Connectivity in the following [guide](https://developers.fireblocks.com/docs/connect-to-exchanges-and-fiat-providers). Endpoint Permission: Admin, Non-Signing Admin. # Get a specific exchange account Source: https://developers.fireblocks.com/api-reference/exchange-accounts/get-a-specific-exchange-account https://swagger.fireblocks.com/openapi.yaml get /exchange_accounts/{exchangeAccountId} Returns an exchange account by ID. Endpoint Permission: Admin, Non-Signing Admin. # Get an asset for an exchange account Source: https://developers.fireblocks.com/api-reference/exchange-accounts/get-an-asset-for-an-exchange-account https://swagger.fireblocks.com/openapi.yaml get /exchange_accounts/{exchangeAccountId}/{assetId} Returns an asset for an exchange account. Endpoint Permission: Admin, Non-Signing Admin. # Get public key to encrypt exchange credentials Source: https://developers.fireblocks.com/api-reference/exchange-accounts/get-public-key-to-encrypt-exchange-credentials https://swagger.fireblocks.com/openapi.yaml get /exchange_accounts/credentials_public_key Return public key # Internal transfer for exchange accounts Source: https://developers.fireblocks.com/api-reference/exchange-accounts/internal-transfer-for-exchange-accounts https://swagger.fireblocks.com/openapi.yaml post /exchange_accounts/{exchangeAccountId}/internal_transfer Transfers funds between trading accounts under the same exchange account. Learn more about Fireblocks Exchange Connectivity in the following [guide](https://developers.fireblocks.com/docs/connect-to-exchanges-and-fiat-providers). Endpoint Permission: Admin, Non-Signing Admin. # List connected exchange accounts Source: https://developers.fireblocks.com/api-reference/exchange-accounts/list-connected-exchange-accounts https://swagger.fireblocks.com/openapi.yaml get /exchange_accounts/paged Returns a list of the connected exchange accounts in your workspace. Endpoint Permission: Admin, Non-Signing Admin. # List exchange accounts Source: https://developers.fireblocks.com/api-reference/exchange-accounts/list-exchange-accounts https://swagger.fireblocks.com/openapi.yaml get /exchange_accounts DEPRECATED - Please use the `/exchange_accounts/paged` endpoint. Endpoint Permission: Admin, Non-Signing Admin. # Add an asset to an external wallet. Source: https://developers.fireblocks.com/api-reference/external-wallets/add-an-asset-to-an-external-wallet https://swagger.fireblocks.com/openapi.yaml post /external_wallets/{walletId}/{assetId} Adds an asset to an existing external wallet. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create an external wallet Source: https://developers.fireblocks.com/api-reference/external-wallets/create-an-external-wallet https://swagger.fireblocks.com/openapi.yaml post /external_wallets Creates a new external wallet with the requested name. External Wallet is a whitelisted address of a wallet that belongs to your users/counterparties. - You cannot see the balance of the external wallet. - You cannot initiate transactions from an external wallet as the source via Fireblocks. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Delete an asset from an external wallet Source: https://developers.fireblocks.com/api-reference/external-wallets/delete-an-asset-from-an-external-wallet https://swagger.fireblocks.com/openapi.yaml delete /external_wallets/{walletId}/{assetId} Deletes an external wallet asset by ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Delete an external wallet Source: https://developers.fireblocks.com/api-reference/external-wallets/delete-an-external-wallet https://swagger.fireblocks.com/openapi.yaml delete /external_wallets/{walletId} Deletes an external wallet by ID. External Wallet is a whitelisted address of a wallet that belongs to your users/counterparties. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Find an external wallet Source: https://developers.fireblocks.com/api-reference/external-wallets/find-an-external-wallet https://swagger.fireblocks.com/openapi.yaml get /external_wallets/{walletId} Returns an external wallet by ID. External Wallet is a whitelisted address of a wallet that belongs to your users/counterparties. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get an asset from an external wallet Source: https://developers.fireblocks.com/api-reference/external-wallets/get-an-asset-from-an-external-wallet https://swagger.fireblocks.com/openapi.yaml get /external_wallets/{walletId}/{assetId} Returns an external wallet by wallet ID and asset ID. External Wallet is a whitelisted address of a wallet that belongs to your users/counterparties. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List external wallets Source: https://developers.fireblocks.com/api-reference/external-wallets/list-external-wallets https://swagger.fireblocks.com/openapi.yaml get /external_wallets Gets a list of external wallets under the workspace. External Wallet is a whitelisted address of a wallet that belongs to your users/counterparties. - You cannot see the balance of the external wallet. - You cannot initiate transactions from an external wallet as the source via Fireblocks. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Set an AML customer reference ID for an external wallet Source: https://developers.fireblocks.com/api-reference/external-wallets/set-an-aml-customer-reference-id-for-an-external-wallet https://swagger.fireblocks.com/openapi.yaml post /external_wallets/{walletId}/set_customer_ref_id Sets an AML/KYT customer reference ID for the specific external wallet. External Wallet is a whitelisted address of a wallet that belongs to your users/counterparties. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Deposit funds from DDA Source: https://developers.fireblocks.com/api-reference/fiat-accounts/deposit-funds-from-dda https://swagger.fireblocks.com/openapi.yaml post /fiat_accounts/{accountId}/deposit_from_linked_dda Deposits funds from the linked DDA. # Find a specific fiat account Source: https://developers.fireblocks.com/api-reference/fiat-accounts/find-a-specific-fiat-account https://swagger.fireblocks.com/openapi.yaml get /fiat_accounts/{accountId} Returns a fiat account by ID. Endpoint Permission: Admin, Non-Signing Admin. # List fiat accounts Source: https://developers.fireblocks.com/api-reference/fiat-accounts/list-fiat-accounts https://swagger.fireblocks.com/openapi.yaml get /fiat_accounts Returns all fiat accounts. Endpoint Permission: Admin, Non-Signing Admin. # Redeem funds to DDA Source: https://developers.fireblocks.com/api-reference/fiat-accounts/redeem-funds-to-dda https://swagger.fireblocks.com/openapi.yaml post /fiat_accounts/{accountId}/redeem_to_linked_dda Redeems funds to the linked DDA. # Edit gas station settings Source: https://developers.fireblocks.com/api-reference/gas-stations/edit-gas-station-settings https://swagger.fireblocks.com/openapi.yaml put /gas_station/configuration Configures gas station settings for ETH. Learn more about the Fireblocks Gas Station in the following [guide](https://developers.fireblocks.com/docs/work-with-gas-station). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Edit gas station settings for an asset Source: https://developers.fireblocks.com/api-reference/gas-stations/edit-gas-station-settings-for-an-asset https://swagger.fireblocks.com/openapi.yaml put /gas_station/configuration/{assetId} Configures gas station settings for a requested asset. Learn more about the Fireblocks Gas Station in the following [guide](https://developers.fireblocks.com/docs/work-with-gas-station). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Get gas station settings Source: https://developers.fireblocks.com/api-reference/gas-stations/get-gas-station-settings https://swagger.fireblocks.com/openapi.yaml get /gas_station Returns gas station settings and ETH balance. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Get gas station settings by asset Source: https://developers.fireblocks.com/api-reference/gas-stations/get-gas-station-settings-by-asset https://swagger.fireblocks.com/openapi.yaml get /gas_station/{assetId} Returns gas station settings and balances for a requested asset. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create a Genie session Source: https://developers.fireblocks.com/api-reference/genie-beta/create-a-genie-session https://swagger.fireblocks.com/openapi.yaml post /genie/sessions Starts a new conversation with Genie, the Fireblocks AI assistant. Returns a `sessionId` — pass it when sending messages, and reuse it across calls to keep one continuous conversation. **Note:** This endpoint is currently in beta and might be subject to changes. # Send a message to a Genie session Source: https://developers.fireblocks.com/api-reference/genie-beta/send-a-message-to-a-genie-session https://swagger.fireblocks.com/openapi.yaml post /genie/sessions/{sessionId}/messages Sends a question to Genie and returns a single answer. Reuse the `sessionId` from the original session on follow-up messages to continue the conversation with prior context. **Note:** This endpoint is currently in beta and might be subject to changes. # Add an asset to an internal wallet Source: https://developers.fireblocks.com/api-reference/internal-wallets/add-an-asset-to-an-internal-wallet https://swagger.fireblocks.com/openapi.yaml post /internal_wallets/{walletId}/{assetId} Adds an asset to an existing internal wallet. # Create an internal wallet Source: https://developers.fireblocks.com/api-reference/internal-wallets/create-an-internal-wallet https://swagger.fireblocks.com/openapi.yaml post /internal_wallets Creates a new internal wallet with the requested name. Learn more about Whitelisted Internal Addresses [here](https://developers.fireblocks.com/docs/whitelist-addresses#internal-wallets) # Delete a whitelisted address Source: https://developers.fireblocks.com/api-reference/internal-wallets/delete-a-whitelisted-address https://swagger.fireblocks.com/openapi.yaml delete /internal_wallets/{walletId}/{assetId} Deletes a whitelisted address (for an asset) from an internal wallet. # Delete an internal wallet Source: https://developers.fireblocks.com/api-reference/internal-wallets/delete-an-internal-wallet https://swagger.fireblocks.com/openapi.yaml delete /internal_wallets/{walletId} Deletes an internal wallet by ID. # Get an asset from an internal wallet Source: https://developers.fireblocks.com/api-reference/internal-wallets/get-an-asset-from-an-internal-wallet https://swagger.fireblocks.com/openapi.yaml get /internal_wallets/{walletId}/{assetId} Returns information for an asset in an internal wallet. # Get assets for internal wallet Source: https://developers.fireblocks.com/api-reference/internal-wallets/get-assets-for-internal-wallet https://swagger.fireblocks.com/openapi.yaml get /internal_wallets/{walletId} Returns information for an internal wallet. # List assets in an internal wallet (Paginated) Source: https://developers.fireblocks.com/api-reference/internal-wallets/list-assets-in-an-internal-wallet-paginated https://swagger.fireblocks.com/openapi.yaml get /internal_wallets/{walletId}/assets Returns a paginated response of assets in an internal wallet. # List internal wallets Source: https://developers.fireblocks.com/api-reference/internal-wallets/list-internal-wallets https://swagger.fireblocks.com/openapi.yaml get /internal_wallets Gets a list of internal wallets. # Set an AML/KYT customer reference ID for internal wallet Source: https://developers.fireblocks.com/api-reference/internal-wallets/set-an-amlkyt-customer-reference-id-for-internal-wallet https://swagger.fireblocks.com/openapi.yaml post /internal_wallets/{walletId}/set_customer_ref_id Sets an AML/KYT customer reference ID for the specific internal wallet. # Add a new signing key Source: https://developers.fireblocks.com/api-reference/key-link-beta/add-a-new-signing-key https://swagger.fireblocks.com/openapi.yaml post /key_link/signing_keys Adds a new signing key to the workspace. The added key will be linked to the specific Fireblocks agent user ID. The same user will receive the proof of ownership message to be signed, and upon successful proof, the key will become enabled. # Add a new validation key Source: https://developers.fireblocks.com/api-reference/key-link-beta/add-a-new-validation-key https://swagger.fireblocks.com/openapi.yaml post /key_link/validation_keys Adds a new validation key used to validate signing keys. The new validation key will undergo an approval process by the workspace quorum. # Disables a validation key Source: https://developers.fireblocks.com/api-reference/key-link-beta/disables-a-validation-key https://swagger.fireblocks.com/openapi.yaml patch /key_link/validation_keys/{keyId} Allows disabling validation key even if it has not expired yet. It is not allowed to enable the validation key back. Another key has to be used for future validations. # Get a bulk registration job Source: https://developers.fireblocks.com/api-reference/key-link-beta/get-a-bulk-registration-job https://swagger.fireblocks.com/openapi.yaml get /key_link/signing_keys/bulk/jobs/{jobId} Returns the progress and outcome of a bulk registration job. `created + alreadyRegistered` reaching `total` means every submitted key is present. # Get a signing key by `keyId` Source: https://developers.fireblocks.com/api-reference/key-link-beta/get-a-signing-key-by-`keyid` https://swagger.fireblocks.com/openapi.yaml get /key_link/signing_keys/{keyId} Returns a signing key if it exists, identified by the specified `keyId`. # Get a validation key by `keyId` Source: https://developers.fireblocks.com/api-reference/key-link-beta/get-a-validation-key-by-`keyid` https://swagger.fireblocks.com/openapi.yaml get /key_link/validation_keys/{keyId} Returns a validation key if it exists, identified by the specified `keyId`. # Get bulk registration jobs Source: https://developers.fireblocks.com/api-reference/key-link-beta/get-bulk-registration-jobs https://swagger.fireblocks.com/openapi.yaml get /key_link/signing_keys/bulk/jobs Returns the workspace's bulk registration jobs, newest first, a page at a time. # Get list of registered validation keys Source: https://developers.fireblocks.com/api-reference/key-link-beta/get-list-of-registered-validation-keys https://swagger.fireblocks.com/openapi.yaml get /key_link/validation_keys Returns the list of validation keys in the workspace # Get list of signing keys Source: https://developers.fireblocks.com/api-reference/key-link-beta/get-list-of-signing-keys https://swagger.fireblocks.com/openapi.yaml get /key_link/signing_keys Returns the list of signing keys in the workspace # Modify the signing keyId Source: https://developers.fireblocks.com/api-reference/key-link-beta/modify-the-signing-keyid https://swagger.fireblocks.com/openapi.yaml patch /key_link/signing_keys/{keyId} Allows assigning the signing key to a vault account, if it hasn't been assigned to any other vault accounts yet. # Register signing keys in bulk Source: https://developers.fireblocks.com/api-reference/key-link-beta/register-signing-keys-in-bulk https://swagger.fireblocks.com/openapi.yaml post /key_link/signing_keys/bulk Accepts a set of signing keys certified by a single validation key and queues them for registration in the background. Returns a job id to poll. A workspace can have one bulk registration at a time; submitting another while one is QUEUED or IN_PROGRESS is rejected with 409. Bulk registrations from different workspaces share a limited number of slots, so a job may wait QUEUED behind another workspace's before it starts. Keys already registered in the workspace are recognized and skipped, so resubmitting the same request after a failure is safe and only processes what is missing. Every key must carry its own proof of ownership; a bulk registration has no interactive step in which one can arrive later. # Set agent user id Source: https://developers.fireblocks.com/api-reference/key-link-beta/set-agent-user-id https://swagger.fireblocks.com/openapi.yaml patch /key_link/signing_keys/{keyId}/agent_user_id Can modify existing signing key id if the key is not enabled. The change done in background and will be visible once applied. If key is already enabled (after proof of ownership) the user cannot be changed. # Get list of mpc keys Source: https://developers.fireblocks.com/api-reference/keys-beta/get-list-of-mpc-keys https://swagger.fireblocks.com/openapi.yaml get /keys/mpc/list Returns a list of MPC signing keys of the workspace. For each key, the list of players associated with it is attached. **Note:** This endpoint is currently in beta and might be subject to changes. # Get list of mpc keys by `userId` Source: https://developers.fireblocks.com/api-reference/keys-beta/get-list-of-mpc-keys-by-`userid` https://swagger.fireblocks.com/openapi.yaml get /keys/mpc/list/{userId} Returns a list of MPC signing keys of a specific user. For each key, the list of players associated with it is attached. **Note:** This endpoint is currently in beta and might be subject to changes. # Create a new network connection Source: https://developers.fireblocks.com/api-reference/network-connections/create-a-new-network-connection https://swagger.fireblocks.com/openapi.yaml post /network_connections Initiates a new network connection. **Note:** This API call is subject to Flexible Routing Schemes. Your routing policy defines how your transactions are routed. You can choose 1 of the 3 different schemes mentioned below for each asset type: - **None**; Defines the profile routing to no destination for that asset type. Incoming transactions to asset types routed to `None` will fail. - **Custom**; Route to an account that you choose. If you remove the account, incoming transactions will fail until you choose another one. - **Default**; Use the routing specified by the network profile the connection is connected to. This scheme is also referred to as "Profile Routing" Default Workspace Presets: - Network Profile Crypto → **Custom** - Network Profile FIAT → **None** - Network Connection Crypto → **Default** - Network Connection FIAT → **Default** Supported asset groups for routing police can be found at `/network_ids/routing_policy_asset_groups` - **Note**: By default, Custom routing scheme uses (`dstId` = `0`, `dstType` = `VAULT`). # Creates a new Network ID Source: https://developers.fireblocks.com/api-reference/network-connections/creates-a-new-network-id https://swagger.fireblocks.com/openapi.yaml post /network_ids Create a new Network ID. # Delete a network connection by ID Source: https://developers.fireblocks.com/api-reference/network-connections/delete-a-network-connection-by-id https://swagger.fireblocks.com/openapi.yaml delete /network_connections/{connectionId} Deletes an existing network connection specified by its connection ID. # Delete specific network ID. Source: https://developers.fireblocks.com/api-reference/network-connections/delete-specific-network-id https://swagger.fireblocks.com/openapi.yaml delete /network_ids/{networkId} Deletes a network by its ID. # Get a network connection Source: https://developers.fireblocks.com/api-reference/network-connections/get-a-network-connection https://swagger.fireblocks.com/openapi.yaml get /network_connections/{connectionId} Gets a network connection by ID. # Get all network IDs Source: https://developers.fireblocks.com/api-reference/network-connections/get-all-network-ids https://swagger.fireblocks.com/openapi.yaml get /network_ids Retrieves a list of all local and discoverable remote network IDs. # Get both local IDs and discoverable remote IDs Source: https://developers.fireblocks.com/api-reference/network-connections/get-both-local-ids-and-discoverable-remote-ids https://swagger.fireblocks.com/openapi.yaml get /network_ids/search Retrieves a list of all local and discoverable remote network IDs. Can be filtered. # List network connections Source: https://developers.fireblocks.com/api-reference/network-connections/list-network-connections https://swagger.fireblocks.com/openapi.yaml get /network_connections Returns all network connections. **Note:** This API call is subject to Flexible Routing Schemes. Your routing policy defines how your transactions are routed. You can choose 1 of the 3 different schemes mentioned below for each asset type: - **None**; Defines the profile routing to no destination for that asset type. Incoming transactions to asset types routed to `None` will fail. - **Custom**; Route to an account that you choose. If you remove the account, incoming transactions will fail until you choose another one. - **Default**; Use the routing specified by the network profile the connection is connected to. This scheme is also referred to as "Profile Routing" Default Workspace Presets: - Network Profile Crypto → **Custom** - Network Profile FIAT → **None** - Network Connection Crypto → **Default** - Network Connection FIAT → **Default** # Retrieve third-party network routing validation Source: https://developers.fireblocks.com/api-reference/network-connections/retrieve-third-party-network-routing-validation https://swagger.fireblocks.com/openapi.yaml get /network_connections/{connectionId}/is_third_party_routing/{assetType} The Fireblocks Network allows for flexibility around incoming deposits. A receiver can receive network deposits to locations other than Fireblocks. This endpoint validates whether future transactions are routed to the displayed recipient or to a 3rd party. # Return all enabled routing policy asset groups Source: https://developers.fireblocks.com/api-reference/network-connections/return-all-enabled-routing-policy-asset-groups https://swagger.fireblocks.com/openapi.yaml get /network_ids/routing_policy_asset_groups Returns all enabled routing policy asset groups # Return specific network ID. Source: https://developers.fireblocks.com/api-reference/network-connections/return-specific-network-id https://swagger.fireblocks.com/openapi.yaml get /network_ids/{networkId} Returns specific network ID. # Update network connection routing policy. Source: https://developers.fireblocks.com/api-reference/network-connections/update-network-connection-routing-policy https://swagger.fireblocks.com/openapi.yaml patch /network_connections/{connectionId}/set_routing_policy Updates an existing network connection's routing policy. # Update network id routing policy. Source: https://developers.fireblocks.com/api-reference/network-connections/update-network-id-routing-policy https://swagger.fireblocks.com/openapi.yaml patch /network_ids/{networkId}/set_routing_policy Updates the routing policy of a specified network ID. # Update network ID's discoverability. Source: https://developers.fireblocks.com/api-reference/network-connections/update-network-ids-discoverability https://swagger.fireblocks.com/openapi.yaml patch /network_ids/{networkId}/set_discoverability Update whether or not the network ID is discoverable by others. # Update network ID's name. Source: https://developers.fireblocks.com/api-reference/network-connections/update-network-ids-name https://swagger.fireblocks.com/openapi.yaml patch /network_ids/{networkId}/set_name Updates name of a specified network ID. # List all distinct owned tokens (paginated) Source: https://developers.fireblocks.com/api-reference/nfts/list-all-distinct-owned-tokens-paginated https://swagger.fireblocks.com/openapi.yaml get /nfts/ownership/assets Returns all owned distinct tokens (for your tenant) and their data in your workspace. # List all owned tokens (paginated) Source: https://developers.fireblocks.com/api-reference/nfts/list-all-owned-tokens-paginated https://swagger.fireblocks.com/openapi.yaml get /nfts/ownership/tokens Returns all tokens and their data in your workspace. # List owned collections (paginated) Source: https://developers.fireblocks.com/api-reference/nfts/list-owned-collections-paginated https://swagger.fireblocks.com/openapi.yaml get /nfts/ownership/collections Returns all collections in your workspace # List token data by ID Source: https://developers.fireblocks.com/api-reference/nfts/list-token-data-by-id https://swagger.fireblocks.com/openapi.yaml get /nfts/tokens/{id} Returns the requested token data. # List tokens by IDs Source: https://developers.fireblocks.com/api-reference/nfts/list-tokens-by-ids https://swagger.fireblocks.com/openapi.yaml get /nfts/tokens Returns the requested tokens data. # Refresh token metadata Source: https://developers.fireblocks.com/api-reference/nfts/refresh-token-metadata https://swagger.fireblocks.com/openapi.yaml put /nfts/tokens/{id} Updates the latest token metadata. # Refresh vault account tokens Source: https://developers.fireblocks.com/api-reference/nfts/refresh-vault-account-tokens https://swagger.fireblocks.com/openapi.yaml put /nfts/ownership/tokens Updates all tokens and balances per blockchain and vault account. Learn more about Fireblocks NFT management in the following [guide](https://developers.fireblocks.com/reference/deploy-an-nft-collection). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Update token ownership status Source: https://developers.fireblocks.com/api-reference/nfts/update-token-ownership-status https://swagger.fireblocks.com/openapi.yaml put /nfts/ownership/tokens/{id}/status Updates token status for a tenant, in all tenant vaults. # Update tokens ownership spam property Source: https://developers.fireblocks.com/api-reference/nfts/update-tokens-ownership-spam-property https://swagger.fireblocks.com/openapi.yaml put /nfts/ownership/tokens/spam Updates tokens spam property for a tenant's token ownerships, in all tenant vaults. # Update tokens ownership status Source: https://developers.fireblocks.com/api-reference/nfts/update-tokens-ownership-status https://swagger.fireblocks.com/openapi.yaml put /nfts/ownership/tokens/status Updates tokens status for a tenant, in all tenant vaults. # Add Collateral Source: https://developers.fireblocks.com/api-reference/off-exchanges/add-collateral https://swagger.fireblocks.com/openapi.yaml post /off_exchange/add Add collateral and create deposit request. Learn more about Fireblocks Off Exchange in the following [guide](https://developers.fireblocks.com/docs/off-exchange). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create Settlement for a Trader Source: https://developers.fireblocks.com/api-reference/off-exchanges/create-settlement-for-a-trader https://swagger.fireblocks.com/openapi.yaml post /off_exchange/settlements/trader Create settlement for a trader. Learn more about Fireblocks Off Exchange in the following [guide](https://developers.fireblocks.com/docs/off-exchange). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Find a specific collateral exchange account Source: https://developers.fireblocks.com/api-reference/off-exchanges/find-a-specific-collateral-exchange-account https://swagger.fireblocks.com/openapi.yaml get /off_exchange/collateral_accounts/{mainExchangeAccountId} Returns a collateral account by mainExchangeAccountId. Learn more about Fireblocks Off Exchange in the following [guide](https://developers.fireblocks.com/docs/off-exchange). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Get Settlements Transactions Source: https://developers.fireblocks.com/api-reference/off-exchanges/get-settlements-transactions https://swagger.fireblocks.com/openapi.yaml get /off_exchange/settlements/transactions Get settlements transactions from exchange. Learn more about Fireblocks Off Exchange in the following [guide](https://developers.fireblocks.com/docs/off-exchange). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Remove Collateral Source: https://developers.fireblocks.com/api-reference/off-exchanges/remove-collateral https://swagger.fireblocks.com/openapi.yaml post /off_exchange/remove Remove collateral, create withdraw request. Learn more about Fireblocks Off Exchange in the following [guide](https://developers.fireblocks.com/docs/off-exchange). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Fetch onchain transactions for a contract Source: https://developers.fireblocks.com/api-reference/onchain-data/fetch-onchain-transactions-for-a-contract https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/transactions Returns a paginated list of onchain transactions for the specified contract address and base asset ID, optionally filtered by date range. # Get historical balance data for a specific account in a contract Source: https://developers.fireblocks.com/api-reference/onchain-data/get-historical-balance-data-for-a-specific-account-in-a-contract https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/account_address/{accountAddress}/balance_history Returns the paginated balance history of the specified account in a contract with optional date range and interval filtering. # Get historical total supply data for a contract Source: https://developers.fireblocks.com/api-reference/onchain-data/get-historical-total-supply-data-for-a-contract https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/total_supply Returns the paginated total supply history of the specified contract with optional date range and interval filtering. # Get latest balances for all addresses holding tokens from a contract Source: https://developers.fireblocks.com/api-reference/onchain-data/get-latest-balances-for-all-addresses-holding-tokens-from-a-contract https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/balances Returns the latest balance for each unique address with support for numeric balance sorting. The `prev` cursor is reserved for future support. # Get summary for the token contract Source: https://developers.fireblocks.com/api-reference/onchain-data/get-summary-for-the-token-contract https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/summary Returns the total number of unique addresses holding balances and the total supply for the specified contract. # Get the current state of addresses in an access registry Source: https://developers.fireblocks.com/api-reference/onchain-data/get-the-current-state-of-addresses-in-an-access-registry https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/access_registry_address/{accessRegistryAddress}/list Returns the current state of addresses in the specified access registry. Only addresses that are currently active (added but not removed) are included. # List of active roles for a given contract address and base asset ID Source: https://developers.fireblocks.com/api-reference/onchain-data/list-of-active-roles-for-a-given-contract-address-and-base-asset-id https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/contract_address/{contractAddress}/roles Returns a list of currently active roles for the specified baseAssetId and contractAddress. # Summary of access registry state Source: https://developers.fireblocks.com/api-reference/onchain-data/summary-of-access-registry-state https://swagger.fireblocks.com/openapi.yaml get /onchain_data/base_asset_id/{baseAssetId}/access_registry_address/{accessRegistryAddress}/summary Returns a summary of the current state of the access registry for the specified baseAssetId and accessRegistryAddress. # Enable or disable transactions to OTA Source: https://developers.fireblocks.com/api-reference/ota-beta/enable-or-disable-transactions-to-ota https://swagger.fireblocks.com/openapi.yaml put /management/ota Enable or disable transactions to One Time Addresses (Non Whitelisted addresses). Learn more about [One Time Addresses](https://support.fireblocks.io/hc/en-us/articles/4409104568338-One-Time-Address-OTA-feature) # Returns current OTA status Source: https://developers.fireblocks.com/api-reference/ota-beta/returns-current-ota-status https://swagger.fireblocks.com/openapi.yaml get /management/ota Returns current OTA status # Create payment flow configuration Source: https://developers.fireblocks.com/api-reference/payments--flows/create-payment-flow-configuration https://swagger.fireblocks.com/openapi.yaml post /payments/workflow_config Generate a new configuration ID to be used for initiating executions in subsequent phases. This configuration should include the operations you intend to incorporate into the workflow, such as TRANSFER, CONVERT, and DISBURSE, in addition to your pre-screening preferences, which are disabled by default. # Create workflow execution Source: https://developers.fireblocks.com/api-reference/payments--flows/create-workflow-execution https://swagger.fireblocks.com/openapi.yaml post /payments/workflow_execution Validate the "workflow-config" previously created by utilizing the unique "configId". This step requires the mandatory field amount, and allows for modifications to other fields defined via the "workflow-config" endpoint, including pre-screening preferences. A response containing the "workflowExecutionId" and detailing the validation status will be provided. Execution is ready when the "workflow-execution" status is READY_FOR_LAUNCH, at which point it can be initiated with "POST /workflow-execution/{workflowExecutionId}/actions/execute". # Delete workflow configuration Source: https://developers.fireblocks.com/api-reference/payments--flows/delete-workflow-configuration https://swagger.fireblocks.com/openapi.yaml delete /payments/workflow_config/{configId} Delete a configuration using the specified "configId". # Execute the payments workflow Source: https://developers.fireblocks.com/api-reference/payments--flows/execute-the-payments-workflow https://swagger.fireblocks.com/openapi.yaml post /payments/workflow_execution/{workflowExecutionId}/actions/execute Launch the execution of a pre-configured workflow, identified by "workflowExecutionId", once it reaches the READY_FOR_LAUNCH state. The workflow undergoes several phases during execution - EXECUTION_IN_PROGRESS - Marks the start of the workflow execution. EXECUTION_COMPLETED or EXECUTION_FAILED - Indicates the execution has reached a final state. # Get workflow execution details Source: https://developers.fireblocks.com/api-reference/payments--flows/get-workflow-execution-details https://swagger.fireblocks.com/openapi.yaml get /payments/workflow_execution/{workflowExecutionId} Retrieve details of a previously initiated workflow execution by specifying the "workflowExecutionId" # Retrieve workflow configuration Source: https://developers.fireblocks.com/api-reference/payments--flows/retrieve-workflow-configuration https://swagger.fireblocks.com/openapi.yaml get /payments/workflow_config/{configId} Retrieve a previously created workflow configuration using the specified "configId". # Create a payout instruction set Source: https://developers.fireblocks.com/api-reference/payments--payout/create-a-payout-instruction-set https://swagger.fireblocks.com/openapi.yaml post /payments/payout **Note:** The reference content in this section documents the Payments Engine endpoint. The Payments Engine endpoints include APIs available only for customers with Payments Engine enabled on their accounts. These endpoints are currently in beta and might be subject to changes. If you want to learn more about Fireblocks Payments Engine, please contact your Fireblocks Customer Success Manager or email CSM@fireblocks.com. **Create a payout instruction set.** A payout instruction set is a set of instructions for distributing payments from a single payment account to a list of payee accounts. The instruction set defines: - the payment account and its account type (vault, exchange, or fiat). - the account type (vault account, exchange account, whitelisted address, network connection, fiat account, or merchant account), the amount, and the asset of payment for each payee account. Learn more about Fireblocks Payments - Payouts in the following [guide](https://developers.fireblocks.com/docs/create-payouts). Endpoint Permission: Admin, Non-Signing Admin. # Execute a payout instruction set Source: https://developers.fireblocks.com/api-reference/payments--payout/execute-a-payout-instruction-set https://swagger.fireblocks.com/openapi.yaml post /payments/payout/{payoutId}/actions/execute **Note:** The reference content in this section documents the Payments Engine endpoint. The Payments Engine endpoints include APIs available only for customers with Payments Engine enabled on their accounts. These endpoints are currently in beta and might be subject to changes. If you want to learn more about Fireblocks Payments Engine, please contact your Fireblocks Customer Success Manager or email CSM@fireblocks.com. **Execute a payout instruction set.** The instruction set will be verified and executed. **Source locking** If you are executing a payout instruction set from a payment account with an already active payout the active payout will complete before the new payout instruction set can be executed. You cannot execute the same payout instruction set more than once. # Get the status of a payout instruction set Source: https://developers.fireblocks.com/api-reference/payments--payout/get-the-status-of-a-payout-instruction-set https://swagger.fireblocks.com/openapi.yaml get /payments/payout/{payoutId} **Note:** The reference content in this section documents the Payments Engine endpoint. The Payments Engine endpoints include APIs available only for customers with Payments Engine enabled on their accounts. These endpoints are currently in beta and might be subject to changes. If you want to learn more about Fireblocks Payments Engine, please contact your Fireblocks Customer Success Manager or email CSM@fireblocks.com. Endpoint Permission: Admin, Non-Signing Admin. # Get the active draft Source: https://developers.fireblocks.com/api-reference/policy-editor-beta/get-the-active-draft https://swagger.fireblocks.com/openapi.yaml get /tap/draft Legacy Endpoint – Returns the active draft and its validation. **Note:** - This endpoint will remain available for the foreseeable future and is not deprecated. - The `getDraft` endpoint under policy/paths provides policy type-specific operations and improved functionality. - These endpoints are currently in beta and might be subject to changes. If you want to participate and learn more about the Fireblocks TAP, please contact your Fireblocks Customer Success Manager or send an email to CSM@fireblocks.com. # Get the active policy and its validation Source: https://developers.fireblocks.com/api-reference/policy-editor-beta/get-the-active-policy-and-its-validation https://swagger.fireblocks.com/openapi.yaml get /tap/active_policy Legacy Endpoint – Returns the active policy and its validation. **Note:** - This endpoint will remain available for the foreseeable future and is not deprecated. - The `getActivePolicy` endpoint under policy/paths provides policy type-specific operations and improved functionality. - These endpoints are currently in beta and might be subject to changes. If you want to participate and learn more about the Fireblocks TAP, please contact your Fireblocks Customer Success Manager or send an email to CSM@fireblocks.com. # Send publish request for a certain draft id Source: https://developers.fireblocks.com/api-reference/policy-editor-beta/send-publish-request-for-a-certain-draft-id https://swagger.fireblocks.com/openapi.yaml post /tap/draft Legacy Endpoint – Send publish request of certain draft id and returns the response. **Note:** - This endpoint will remain available for the foreseeable future and is not deprecated. - The `publishDraft` endpoint under policy/paths provides improved functionality and better performance. - These endpoints are currently in beta and might be subject to changes. If you want to participate and learn more about the Fireblocks TAP, please contact your Fireblocks Customer Success Manager or send an email to CSM@fireblocks.com. # Send publish request for a set of policy rules Source: https://developers.fireblocks.com/api-reference/policy-editor-beta/send-publish-request-for-a-set-of-policy-rules https://swagger.fireblocks.com/openapi.yaml post /tap/publish Send publish request of set of policy rules and returns the response. **Note:** These endpoints are currently in beta and might be subject to changes. If you want to participate and learn more about the Fireblocks TAP, please contact your Fireblocks Customer Success Manager or send an email to CSM@fireblocks.com. # Update the draft with a new set of rules Source: https://developers.fireblocks.com/api-reference/policy-editor-beta/update-the-draft-with-a-new-set-of-rules https://swagger.fireblocks.com/openapi.yaml put /tap/draft Legacy Endpoint – Update the draft and return its validation. **Note:** - This endpoint will remain available for the foreseeable future and is not deprecated. - The `updateDraft` endpoint under policy/paths provides policy type-specific operations and improved functionality. - These endpoints are currently in beta and might be subject to changes. If you want to participate and learn more about the Fireblocks TAP, please contact your Fireblocks Customer Success Manager or send an email to CSM@fireblocks.com. # Calculate the AOT quota for a policy rule Source: https://developers.fireblocks.com/api-reference/policy-editor-v2-beta/calculate-the-aot-quota-for-a-policy-rule https://swagger.fireblocks.com/openapi.yaml post /policy/rules/quota Returns the Amount Over Time (AOT) quota calculated for a specific policy rule. Endpoint Permissions: Owner, Admin, Non-Signing Admin. # Get the active draft by policy type Source: https://developers.fireblocks.com/api-reference/policy-editor-v2/get-the-active-draft-by-policy-type https://swagger.fireblocks.com/openapi.yaml get /policy/draft Returns the active draft and its validation for a specific policy type. # Get the active policy and its validation by policy type Source: https://developers.fireblocks.com/api-reference/policy-editor-v2/get-the-active-policy-and-its-validation-by-policy-type https://swagger.fireblocks.com/openapi.yaml get /policy/active_policy Returns the active policy and its validation for a specific policy type. Endpoint Permissions: Owner, Admin, Non-Signing Admin. # Send publish request for a certain draft id Source: https://developers.fireblocks.com/api-reference/policy-editor-v2/send-publish-request-for-a-certain-draft-id https://swagger.fireblocks.com/openapi.yaml post /policy/draft Send publish request of certain draft id and returns the response. **⚠️ IMPORTANT SECURITY NOTICE:** The Fireblocks Policy is a critical security guardrail. Programmatically modifying your policy via the API introduces significant security risks. As an industry best practice, Fireblocks strongly recommends manual editing accompanied by strict human oversight and approval workflows for all policy changes. Programmatic updates should only be implemented by advanced users with comprehensive, multi-layer security controls in place. If you want to learn more about the Fireblocks Policy Editor, please contact your Fireblocks Customer Success Manager or send an email to CSM@fireblocks.com. # Update the draft with a new set of rules by policy types Source: https://developers.fireblocks.com/api-reference/policy-editor-v2/update-the-draft-with-a-new-set-of-rules-by-policy-types https://swagger.fireblocks.com/openapi.yaml put /policy/draft Update the draft and return its validation for specific policy types. **⚠️ IMPORTANT SECURITY NOTICE:** The Fireblocks Policy is a critical security guardrail. Programmatically modifying your policy via the API introduces significant security risks. As an industry best practice, Fireblocks strongly recommends manual editing accompanied by strict human oversight and approval workflows for all policy changes. Programmatic updates should only be implemented by advanced users with comprehensive, multi-layer security controls in place. # Create a report Source: https://developers.fireblocks.com/api-reference/reports-beta/create-a-report https://swagger.fireblocks.com/openapi.yaml post /reports Creates a new asynchronous report job and returns a receipt containing the report ID. Poll `GET /v1/reports/{reportId}` to check status. When `status` is `COMPLETED`, the poll response includes a fresh pre-signed download URL in `links.downloadUrl`. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Viewer and above. # Get report status Source: https://developers.fireblocks.com/api-reference/reports-beta/get-report-status https://swagger.fireblocks.com/openapi.yaml get /reports/{reportId} Returns the current status of a report job. Check the `status` field to determine the outcome. When `status` is `COMPLETED`, the response includes a fresh pre-signed download URL in `links.downloadUrl` (TTL ~1 hour). Re-poll this endpoint to obtain a new URL after expiry. When `status` is `FAILED`, the `failedAt` timestamp indicates when the failure occurred. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Viewer and above. # List reports Source: https://developers.fireblocks.com/api-reference/reports-beta/list-reports https://swagger.fireblocks.com/openapi.yaml get /reports Returns a paginated list of report jobs scoped to the calling tenant. **Note:** Download URLs are not included in list responses. Call `GET /v1/reports/{reportId}` to obtain a fresh signed download URL for a specific completed report. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Viewer and above. # Resets device Source: https://developers.fireblocks.com/api-reference/reset-device/resets-device https://swagger.fireblocks.com/openapi.yaml post /management/users/{id}/reset_device Resets mobile device for given console user, that user will need to do mobile onboarding again. - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Endpoint Permission: Admin, Non-Signing Admin. # Get a FSPM security finding by ID Source: https://developers.fireblocks.com/api-reference/security-posture-management/get-a-fspm-security-finding-by-id https://swagger.fireblocks.com/openapi.yaml get /security/fspm/findings/{id} Returns a single FSPM security finding for the workspace, redacted to the public field set. Endpoint Roles: Security Admin, Security Auditor. **Note:** This endpoint is available only for the FSPM Pro package. It is not available for FSPM Basic. # Get FSPM security findings Source: https://developers.fireblocks.com/api-reference/security-posture-management/get-fspm-security-findings https://swagger.fireblocks.com/openapi.yaml get /security/fspm/findings Returns a paginated list of FSPM security findings for the workspace. Endpoint Roles: Security Admin, Security Auditor. **Note:** This endpoint is available only for the FSPM Pro package. It is not available for FSPM Basic. # Update a FSPM security finding by ID Source: https://developers.fireblocks.com/api-reference/security-posture-management/update-a-fspm-security-finding-by-id https://swagger.fireblocks.com/openapi.yaml patch /security/fspm/findings/{id} Accepts or reopens a finding for the workspace. When accepting a finding (`status: "ACCEPTED"`), `statusUpdatedReason` is required. Endpoint Roles: Security Admin. **Note:** This endpoint is available only for the FSPM Pro package. It is not available for FSPM Basic. # Add external ref. ID Source: https://developers.fireblocks.com/api-reference/smart-transfer/add-external-ref-id https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/external-id Set external id Smart Transfer ticket. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Cancel Ticket Source: https://developers.fireblocks.com/api-reference/smart-transfer/cancel-ticket https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/cancel Cancel Smart Transfer ticket. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create leg (term) Source: https://developers.fireblocks.com/api-reference/smart-transfer/create-leg-term https://swagger.fireblocks.com/openapi.yaml post /smart-transfers/{ticketId}/terms Creates new smart transfer ticket term (when the ticket status is DRAFT). Learn more about Fireblocks Smart Transfers in the following [guide](https://developers.fireblocks.com/docs/execute-smart-transfers). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create Ticket Source: https://developers.fireblocks.com/api-reference/smart-transfer/create-ticket https://swagger.fireblocks.com/openapi.yaml post /smart-transfers Creates a new Smart Transfer ticket. Learn more about Fireblocks Smart Transfers [here](https://developers.fireblocks.com/docs/execute-smart-transfers). **Note:** The `DVP` value is in Early Access and should only be used if Fireblocks has enabled it in your workspace. Contact your Customer Success Manager for more information. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor. # Define funding source Source: https://developers.fireblocks.com/api-reference/smart-transfer/define-funding-source https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/terms/{termId}/fund Set funding source for ticket term (in case of ASYNC tickets, this will execute transfer immediately). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Delete ticket leg (term) Source: https://developers.fireblocks.com/api-reference/smart-transfer/delete-ticket-leg-term https://swagger.fireblocks.com/openapi.yaml delete /smart-transfers/{ticketId}/terms/{termId} Delete ticket term when ticket is in DRAFT status # Find Ticket Source: https://developers.fireblocks.com/api-reference/smart-transfer/find-ticket https://swagger.fireblocks.com/openapi.yaml get /smart-transfers Find tickets by their title or ticker. You can also query all tickets without filters by not providing any input parameters. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Fund dvp ticket Source: https://developers.fireblocks.com/api-reference/smart-transfer/fund-dvp-ticket https://swagger.fireblocks.com/openapi.yaml put /smart_transfers/{ticketId}/dvp/fund Create or fulfill dvp ticket order # Fund ticket manually Source: https://developers.fireblocks.com/api-reference/smart-transfer/fund-ticket-manually https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/fulfill Manually fulfill ticket, in case when all terms (legs) are funded manually. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Get Smart Transfer ticket term Source: https://developers.fireblocks.com/api-reference/smart-transfer/get-smart-transfer-ticket-term https://swagger.fireblocks.com/openapi.yaml get /smart-transfers/{ticketId}/terms/{termId} Find a specific term of a specific Smart Transfer ticket. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get smart transfers statistic Source: https://developers.fireblocks.com/api-reference/smart-transfer/get-smart-transfers-statistic https://swagger.fireblocks.com/openapi.yaml get /smart_transfers/statistic Get smart transfer statistic # Get user group Source: https://developers.fireblocks.com/api-reference/smart-transfer/get-user-group https://swagger.fireblocks.com/openapi.yaml get /smart-transfers/settings/user-groups Get Smart Transfer user groups. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Manually add term transaction Source: https://developers.fireblocks.com/api-reference/smart-transfer/manually-add-term-transaction https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/terms/{termId}/manually-fund Manually set ticket term transaction. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Search Ticket by ID Source: https://developers.fireblocks.com/api-reference/smart-transfer/search-ticket-by-id https://swagger.fireblocks.com/openapi.yaml get /smart-transfers/{ticketId} Find Smart Transfer ticket by id. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Set expiration Source: https://developers.fireblocks.com/api-reference/smart-transfer/set-expiration https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/expires-in Set expiration date on Smart Transfer ticket. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Set funding source and approval Source: https://developers.fireblocks.com/api-reference/smart-transfer/set-funding-source-and-approval https://swagger.fireblocks.com/openapi.yaml put /smart_transfers/{ticketId}/terms/{termId}/dvp/approve Set funding source for ticket term and creating approving transaction for contract to transfer asset # Set user group Source: https://developers.fireblocks.com/api-reference/smart-transfer/set-user-group https://swagger.fireblocks.com/openapi.yaml post /smart-transfers/settings/user-groups Set Smart Transfers user group to receive email notifications for Smart Transfers. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Submit ticket Source: https://developers.fireblocks.com/api-reference/smart-transfer/submit-ticket https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/submit Submit Smart Transfer ticket - change status into ready for approval if auto approval is not turned on, or OPEN if auto approval is on. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Update ticket leg (term) Source: https://developers.fireblocks.com/api-reference/smart-transfer/update-ticket-leg-term https://swagger.fireblocks.com/openapi.yaml put /smart-transfers/{ticketId}/terms/{termId} Update ticket term (when ticket status is DRAFT) # Approve provider terms of service Source: https://developers.fireblocks.com/api-reference/staking/approve-provider-terms-of-service https://swagger.fireblocks.com/openapi.yaml post /staking/providers/{providerId}/approveTermsOfService Approves the provider's terms of service. Must be called once before performing any staking operation with this provider. # Claim accrued rewards Source: https://developers.fireblocks.com/api-reference/staking/claim-accrued-rewards https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/claim_rewards Claims available staking rewards for the specified chain and vault. Supported chains: Solana and Polygon (POL/Matic). Behavior depends on protocol reward distribution. # Consolidate staking positions (ETH validator consolidation) Source: https://developers.fireblocks.com/api-reference/staking/consolidate-staking-positions-eth-validator-consolidation https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/consolidate Consolidates the source staking position into the destination, merging the balance into the destination and closing the source position once complete. Both positions must be from the same vault account (i.e. same withdrawal credentials). On chain, this translates into a consolidation transaction, where the source validator is consolidated into the destination validator. Supported chains: Ethereum (ETH) only. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor. **Note:** This endpoint is currently in beta and might be subject to changes. # Get chain-level staking parameters Source: https://developers.fireblocks.com/api-reference/staking/get-chain-level-staking-parameters https://swagger.fireblocks.com/openapi.yaml get /staking/chains/{chainDescriptor}/chainInfo Returns chain-specific staking information such as epoch/slot cadence, lockup or unbonding periods, fee/reward mechanics, and other operational constraints. # Get position details Source: https://developers.fireblocks.com/api-reference/staking/get-position-details https://swagger.fireblocks.com/openapi.yaml get /staking/positions/{id} Returns full details for a single staking position: amounts, rewards, status, chain, and vault. # Get positions summary Source: https://developers.fireblocks.com/api-reference/staking/get-positions-summary https://swagger.fireblocks.com/openapi.yaml get /staking/positions/summary Returns an aggregated cross-vault summary: active/inactive counts, total staked, and total rewards per chain. # Get positions summary by vault Source: https://developers.fireblocks.com/api-reference/staking/get-positions-summary-by-vault https://swagger.fireblocks.com/openapi.yaml get /staking/positions/summary/vaults Returns per-vault aggregates: status breakdown, total staked, and total rewards per chain. # Initiate or add to existing stake Source: https://developers.fireblocks.com/api-reference/staking/initiate-or-add-to-existing-stake https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/stake Creates a new staking position and returns its unique ID. For Ethereum compounding validator (EIP-7251): when the 'id' of an existing compounding validator position is provided, adds to that position; otherwise creates a new position. For Ethereum legacy validator: creates a new position regardless of existing delegations. For Cosmos chains and Ethereum liquid staking (Lido): automatically add to existing positions for the same validator provider and same vault account if one exists, otherwise create a new position. For Solana and Polygon (MATIC/POL): always create new positions regardless of existing delegations. # Initiate unstake Source: https://developers.fireblocks.com/api-reference/staking/initiate-unstake https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/unstake Submits a chain-specific unstake request. # List related transactions for a position Source: https://developers.fireblocks.com/api-reference/staking/list-related-transactions-for-a-position https://swagger.fireblocks.com/openapi.yaml get /staking/positions/{id}/related_transactions Returns enriched transaction history for a staking position with cursor-based pagination. Includes in-flight transactions with status pending. The in-flight transaction is always returned first; completed and failed history is ordered by the order parameter. # List staking positions Source: https://developers.fireblocks.com/api-reference/staking/list-staking-positions https://swagger.fireblocks.com/openapi.yaml get /staking/positions Returns all staking positions with core details: amounts, rewards, status, chain, and vault. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List staking positions (Paginated) Source: https://developers.fireblocks.com/api-reference/staking/list-staking-positions-paginated https://swagger.fireblocks.com/openapi.yaml get /staking/positions_paginated Returns staking positions with core details: amounts, rewards, status, chain, and vault. It supports cursor-based pagination for efficient data retrieval. This endpoint always returns a paginated response with {data, next} structure. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List staking providers Source: https://developers.fireblocks.com/api-reference/staking/list-staking-providers https://swagger.fireblocks.com/openapi.yaml get /staking/providers Returns all available staking providers with metadata such as name, ID, and supported chains. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List supported staking chains Source: https://developers.fireblocks.com/api-reference/staking/list-supported-staking-chains https://swagger.fireblocks.com/openapi.yaml get /staking/chains Returns an alphabetical list of blockchains supported for staking by the current workspace context. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Merge staking positions Source: https://developers.fireblocks.com/api-reference/staking/merge-staking-positions https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/merge Merges the source stake account into the destination, consolidating the balance into the destination and closing the source account once complete. Both accounts must be from the same validator provider and of same vault account.. Supported chains: Solana (SOL). Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor. # Split a staking position Source: https://developers.fireblocks.com/api-reference/staking/split-a-staking-position https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/split Splits a staking position by creating a new stake account with the requested amount, while keeping the original account with the remaining balance. Supported chains: Solana (SOL). # Withdraw staked funds Source: https://developers.fireblocks.com/api-reference/staking/withdraw-staked-funds https://swagger.fireblocks.com/openapi.yaml post /staking/chains/{chainDescriptor}/withdraw Withdraws funds that have completed the unbonding period. Typically requires the position to be deactivated first (unstake → unbond → withdraw). Amount and timing vary by chain protocol. Partial withdrawal is supported for ETH compounding validators (EIP-7251/Pectra) and Cosmos chains via the optional 'amount' field. For ETH compounding validators, the remaining balance must be at least 32 ETH after the withdrawal. For all other chains, omitting 'amount' withdraws the entire available balance. # Create a new tag Source: https://developers.fireblocks.com/api-reference/tags/create-a-new-tag https://swagger.fireblocks.com/openapi.yaml post /tags Create a new tag. Endpoint Permissions: For protected tags: ADMIN,NON_SIGNING_ADMIN,OWNER. For non protected tags: ADMIN,NON_SIGNING_ADMIN,OWNER,SIGNER,EDITOR,APPROVER. # Delete a tag Source: https://developers.fireblocks.com/api-reference/tags/delete-a-tag https://swagger.fireblocks.com/openapi.yaml delete /tags/{tagId} Delete the specified tag. Endpoint Permission: For protected tags: Owner, Admin, Non-Signing Admin. For non protected tags: Owner, Admin, Non-Signing Admin, Signer, Editor, Approver. # Get a tag Source: https://developers.fireblocks.com/api-reference/tags/get-a-tag https://swagger.fireblocks.com/openapi.yaml get /tags/{tagId} Retrieve an existing tag by ID. # Get an approval request by id Source: https://developers.fireblocks.com/api-reference/tags/get-an-approval-request-by-id https://swagger.fireblocks.com/openapi.yaml get /tags/approval_requests/{id} Get an approval request by id # Get list of tags Source: https://developers.fireblocks.com/api-reference/tags/get-list-of-tags https://swagger.fireblocks.com/openapi.yaml get /tags Retrieve a paged list of all tags according to filters. # Update a tag Source: https://developers.fireblocks.com/api-reference/tags/update-a-tag https://swagger.fireblocks.com/openapi.yaml patch /tags/{tagId} Update an existing specified tag. Endpoint Permission: For protected tags: Owner, Admin, Non-Signing Admin. For non protected tags: Owner, Admin, Non-Signing Admin, Signer, Editor, Approver. # Create a Tempo transfer transaction Source: https://developers.fireblocks.com/api-reference/tempo-beta/create-a-tempo-transfer-transaction https://swagger.fireblocks.com/openapi.yaml post /operations/tempo/transfer Creates a new Tempo transfer transaction. # Burn tokens Source: https://developers.fireblocks.com/api-reference/tokenization/burn-tokens https://swagger.fireblocks.com/openapi.yaml post /tokenization/collections/{id}/tokens/burn Burn tokens in a collection # Create a new collection Source: https://developers.fireblocks.com/api-reference/tokenization/create-a-new-collection https://swagger.fireblocks.com/openapi.yaml post /tokenization/collections Create a new collection and link it as a token. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, and Editor. # Delete a collection link Source: https://developers.fireblocks.com/api-reference/tokenization/delete-a-collection-link https://swagger.fireblocks.com/openapi.yaml delete /tokenization/collections/{id} Delete a collection link # Deploy LayerZero adapters Source: https://developers.fireblocks.com/api-reference/tokenization/deploy-layerzero-adapters https://swagger.fireblocks.com/openapi.yaml post /tokenization/multichain/bridge/layerzero Deploy LayerZero adapters for multichain token bridging functionality. This endpoint creates adapter contracts that enable cross-chain token transfers. # Get a collection by id Source: https://developers.fireblocks.com/api-reference/tokenization/get-a-collection-by-id https://swagger.fireblocks.com/openapi.yaml get /tokenization/collections/{id} Get a collection by id # Get active RBAC roles for a token Source: https://developers.fireblocks.com/api-reference/tokenization/get-active-rbac-roles-for-a-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/rbac Returns a list of currently active roles for the token contract. # Get balance history for a specific account Source: https://developers.fireblocks.com/api-reference/tokenization/get-balance-history-for-a-specific-account https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/balances/{accountAddress}/history Returns paginated balance history for the specified account address with optional time-range filtering. # Get collection token details Source: https://developers.fireblocks.com/api-reference/tokenization/get-collection-token-details https://swagger.fireblocks.com/openapi.yaml get /tokenization/collections/{id}/tokens/{tokenId} Get collection token details by id # Get collections Source: https://developers.fireblocks.com/api-reference/tokenization/get-collections https://swagger.fireblocks.com/openapi.yaml get /tokenization/collections Get collections (paginated). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get current state of addresses in an access registry Source: https://developers.fireblocks.com/api-reference/tokenization/get-current-state-of-addresses-in-an-access-registry https://swagger.fireblocks.com/openapi.yaml get /tokenization/access_registries/{id}/addresses Returns the currently active addresses in the access registry (added but not removed). # Get deterministic address for contract deployment Source: https://developers.fireblocks.com/api-reference/tokenization/get-deterministic-address-for-contract-deployment https://swagger.fireblocks.com/openapi.yaml post /tokenization/multichain/deterministic_address Get a deterministic address for contract deployment. The address is derived from the contract's bytecode and provided salt. This endpoint is used to get the address of a contract that will be deployed in the future. # Get historical total supply for a token Source: https://developers.fireblocks.com/api-reference/tokenization/get-historical-total-supply-for-a-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/total_supply Returns paginated total supply history for the token contract with optional time-range filtering and binning. # Get latest balances for all holders of a token Source: https://developers.fireblocks.com/api-reference/tokenization/get-latest-balances-for-all-holders-of-a-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/balances Returns the latest balance for each unique address holding this token. # Get LayerZero DVN configuration Source: https://developers.fireblocks.com/api-reference/tokenization/get-layerzero-dvn-configuration https://swagger.fireblocks.com/openapi.yaml get /tokenization/multichain/bridge/layerzero/config/{adapterTokenLinkId}/dvns Retrieve the DVN (Data Verification Network) configuration for a specific adapter. Returns DVN configurations for channels between the source adapter and its peers. # Get LayerZero peers Source: https://developers.fireblocks.com/api-reference/tokenization/get-layerzero-peers https://swagger.fireblocks.com/openapi.yaml get /tokenization/multichain/bridge/layerzero/config/{adapterTokenLinkId}/peers Retrieve the LayerZero peers configured for a specific adapter. Returns information about peer relationships for cross-chain communication. # Get onchain summary for a token Source: https://developers.fireblocks.com/api-reference/tokenization/get-onchain-summary-for-a-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/summary Returns the total number of unique holders and the total supply for the token contract. # Get onchain transactions for a token Source: https://developers.fireblocks.com/api-reference/tokenization/get-onchain-transactions-for-a-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/transactions Returns a paginated list of onchain transactions for the token contract, optionally filtered by date range. # Get onchain transfers for a token Source: https://developers.fireblocks.com/api-reference/tokenization/get-onchain-transfers-for-a-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/transfers Returns a paginated list of ERC20 transfer events for the token contract, optionally filtered by date range. # Get summary of an access registry Source: https://developers.fireblocks.com/api-reference/tokenization/get-summary-of-an-access-registry https://swagger.fireblocks.com/openapi.yaml get /tokenization/access_registries/{id}/summary Returns a summary of the current state of the access registry. # Get the latest balance for a specific account Source: https://developers.fireblocks.com/api-reference/tokenization/get-the-latest-balance-for-a-specific-account https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id}/balances/{accountAddress} Returns the latest token balance for the specified account address. # Get the total count of linked tokens Source: https://developers.fireblocks.com/api-reference/tokenization/get-the-total-count-of-linked-tokens https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/count Get the total count of linked tokens # Issue a new token Source: https://developers.fireblocks.com/api-reference/tokenization/issue-a-new-token https://swagger.fireblocks.com/openapi.yaml post /tokenization/tokens Facilitates the creation of a new token, supporting both EVM-based and Stellar/Ripple platforms. For EVM, it deploys the corresponding contract template to the blockchain and links the token to the workspace. For Stellar/Ripple, it links a newly created token directly to the workspace without deploying a contract. Returns the token link with status "PENDING" until the token is deployed or "SUCCESS" if no deployment is needed. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, and Editor. # Issue a token on one or more blockchains Source: https://developers.fireblocks.com/api-reference/tokenization/issue-a-token-on-one-or-more-blockchains https://swagger.fireblocks.com/openapi.yaml post /tokenization/multichain/tokens Facilitates the creation of a new token on one or more blockchains. # Link a contract Source: https://developers.fireblocks.com/api-reference/tokenization/link-a-contract https://swagger.fireblocks.com/openapi.yaml post /tokenization/tokens/link Link an a contract # List all linked tokens Source: https://developers.fireblocks.com/api-reference/tokenization/list-all-linked-tokens https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens Return all linked tokens (paginated) # Mint tokens Source: https://developers.fireblocks.com/api-reference/tokenization/mint-tokens https://swagger.fireblocks.com/openapi.yaml post /tokenization/collections/{id}/tokens/mint Mint tokens and upload metadata # Reissue a multichain token Source: https://developers.fireblocks.com/api-reference/tokenization/reissue-a-multichain-token https://swagger.fireblocks.com/openapi.yaml post /tokenization/multichain/reissue/token/{tokenLinkId} Reissue a multichain token. This endpoint allows you to reissue a token on one or more blockchains. The token must be initially issued using the issueTokenMultiChain endpoint. # Remove LayerZero adapters Source: https://developers.fireblocks.com/api-reference/tokenization/remove-layerzero-adapters https://swagger.fireblocks.com/openapi.yaml delete /tokenization/multichain/bridge/layerzero Remove LayerZero adapters by deactivating and unlinking them. This endpoint revokes roles and deactivates the specified adapter contracts. # Remove LayerZero peers Source: https://developers.fireblocks.com/api-reference/tokenization/remove-layerzero-peers https://swagger.fireblocks.com/openapi.yaml delete /tokenization/multichain/bridge/layerzero/config/peers Remove LayerZero peers to disconnect adapter contracts. This endpoint removes peer relationships between LayerZero adapters. # Return a linked token Source: https://developers.fireblocks.com/api-reference/tokenization/return-a-linked-token https://swagger.fireblocks.com/openapi.yaml get /tokenization/tokens/{id} Return a linked token, with its status and metadata. # Set LayerZero DVN configuration Source: https://developers.fireblocks.com/api-reference/tokenization/set-layerzero-dvn-configuration https://swagger.fireblocks.com/openapi.yaml post /tokenization/multichain/bridge/layerzero/config/dvns Configure DVN settings for LayerZero adapters. This endpoint sets up the DVN configuration for message verification between source and destination adapters. # Set LayerZero peers Source: https://developers.fireblocks.com/api-reference/tokenization/set-layerzero-peers https://swagger.fireblocks.com/openapi.yaml post /tokenization/multichain/bridge/layerzero/config/peers Set LayerZero peers to establish connections between adapter contracts. This endpoint creates peer relationships that enable cross-chain communication. It sets the destination adapter as a peer of the source adapter. If `bidirectional` is true, it also sets the source adapter as a peer of the destination adapter(s). # Unlink a token Source: https://developers.fireblocks.com/api-reference/tokenization/unlink-a-token https://swagger.fireblocks.com/openapi.yaml delete /tokenization/tokens/{id} Unlink a token. The token will be unlinked from the workspace. The token will not be deleted on chain nor the refId, only the link to the workspace will be removed. # Validate LayerZero channel configuration Source: https://developers.fireblocks.com/api-reference/tokenization/validate-layerzero-channel-configuration https://swagger.fireblocks.com/openapi.yaml get /tokenization/multichain/bridge/layerzero/validate Validate the LayerZero channel configuration between adapters. This endpoint checks if the channel configuration is correct and returns any validation errors. # Create a quote Source: https://developers.fireblocks.com/api-reference/trading/create-a-quote https://swagger.fireblocks.com/openapi.yaml post /trading/quotes Generate a time-limited quote for asset conversion, providing exchange rate and amount calculations. If `side` is omitted it defaults to `SELL`. Use `side: BOTH` to get quotes for each side the provider supports in a single call; providers that support only one side return that side only. Every returned quote carries a concrete `BUY` or `SELL` side, never `BOTH`. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Editor. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Create an order Source: https://developers.fireblocks.com/api-reference/trading/create-an-order https://swagger.fireblocks.com/openapi.yaml post /trading/orders Create an order to buy or sell an asset. If no source is given, an external source will be use. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Editor. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get all offers Source: https://developers.fireblocks.com/api-reference/trading/get-all-offers https://swagger.fireblocks.com/openapi.yaml post /trading/offers Retrieve all available offers across the workspace for a given asset pair. Always operates in open scope — no provider or account selection required. Returns a mix of indicative rates and committed quotes as applicable per provider. If `side` is omitted it defaults to `SELL`. Use `side: BOTH` to get offers for each side the provider supports in a single call; providers that support only one side return that side only. Every returned offer carries a concrete `BUY` or `SELL` side, never `BOTH`. If no slippageBps is provided, it defaults to 50 bps (0.5%). Slippage and settlement configuration do not affect the returned rate. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Editor. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get order details Source: https://developers.fireblocks.com/api-reference/trading/get-order-details https://swagger.fireblocks.com/openapi.yaml get /trading/orders/{orderId} Retrieve detailed information about a specific order by its ID. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get order requirement details for an order Source: https://developers.fireblocks.com/api-reference/trading/get-order-requirement-details-for-an-order https://swagger.fireblocks.com/openapi.yaml get /trading/orders/{orderId}/requirement Fetch order requirement details for an order that is in `AWAITING_INFORMATION` status. The response includes `requirementId` and `dueBy` metadata, a `requiredData` JSON Schema (Draft-7) describing the shape of the `data` object expected on `POST /trading/orders/{orderId}/requirement/data`, and `requiredFiles` descriptors for any files the provider requires (uploaded via `POST /trading/orders/{orderId}/requirement/file`). Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get orders Source: https://developers.fireblocks.com/api-reference/trading/get-orders https://swagger.fireblocks.com/openapi.yaml get /trading/orders Retrieve a paginated list of orders with optional filtering by account, provider, status, and time range. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get providers Source: https://developers.fireblocks.com/api-reference/trading/get-providers https://swagger.fireblocks.com/openapi.yaml get /trading/providers Retrieve a list of all available external providers supporting trading activities through the platform. **Endpoint Permission:** Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get rates Source: https://developers.fireblocks.com/api-reference/trading/get-rates https://swagger.fireblocks.com/openapi.yaml post /trading/rates Retrieve indicative exchange rate from specified providers for a given asset pair. Rates are non-executable price signals intended for discovery and display purposes. If `side` is omitted it defaults to `SELL`. Use `side: BOTH` to get rates for each side the provider supports in a single call; providers that support only one side return that side only. Every returned rate carries a concrete `BUY` or `SELL` side, never `BOTH`. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Get trading provider by ID Source: https://developers.fireblocks.com/api-reference/trading/get-trading-provider-by-id https://swagger.fireblocks.com/openapi.yaml get /trading/providers/{providerId} Retrieve a single provider by ID. **Endpoint Permission:** Owner, Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Submit a response to an order requirement Source: https://developers.fireblocks.com/api-reference/trading/submit-a-response-to-an-order-requirement https://swagger.fireblocks.com/openapi.yaml post /trading/orders/{orderId}/requirement/data Submit the user's textual response to an order requirement on an order that is in `AWAITING_INFORMATION` status. The request body carries `data` — a free-form object conforming to the `requiredData` JSON Schema returned by the GET endpoint. Any required files are uploaded separately via `POST /trading/orders/{orderId}/requirement/file`. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Editor. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Upload a file for an order requirement Source: https://developers.fireblocks.com/api-reference/trading/upload-a-file-for-an-order-requirement https://swagger.fireblocks.com/openapi.yaml post /trading/orders/{orderId}/requirement/file Upload a single file (multipart/form-data) in response to an order requirement on an order that is in `AWAITING_INFORMATION` status. Call this endpoint once per required file. Send `fileKey` (matching a `fileKey` from `requiredFiles` on the GET response) and the binary `file`. Its type must be one of the supported file formats. Fireblocks encrypts each file and uploads it individually to the underlying provider. The textual response is submitted separately via `POST /trading/orders/{orderId}/requirement/data`. Endpoint Permission: Owner, Admin, Non-Signing Admin, Signer, Editor. For detailed information about error codes and troubleshooting, please refer to our [API Error Codes documentation](https://developers.fireblocks.com/reference/api-error-codes). # Cancel a transaction Source: https://developers.fireblocks.com/api-reference/transactions/cancel-a-transaction https://swagger.fireblocks.com/openapi.yaml post /transactions/{txId}/cancel Cancels a transaction by Fireblocks Transaction ID. Can be used only for transactions that did not get to the BROADCASTING state. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create a new transaction Source: https://developers.fireblocks.com/api-reference/transactions/create-a-new-transaction https://swagger.fireblocks.com/openapi.yaml post /transactions Creates a new transaction. This endpoint can be used for regular Transfers, Contract Calls, Raw & Typed message signing. - For Transfers, the required parameters are: `assetId`, `source`, `destination` and `amount`. - For Contract Calls, the required parameters are: `operation.CONTRACT_CALL`, `assetId` (Base Asset), `source`, # Drop ETH (EVM) transaction by ID Source: https://developers.fireblocks.com/api-reference/transactions/drop-eth-evm-transaction-by-id https://swagger.fireblocks.com/openapi.yaml post /transactions/{txId}/drop Drops a stuck ETH (EVM) transaction and creates a replacement transaction with 0 amount. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Estimate the required fee for an asset Source: https://developers.fireblocks.com/api-reference/transactions/estimate-the-required-fee-for-an-asset https://swagger.fireblocks.com/openapi.yaml get /estimate_network_fee Gets the estimated required fee for an asset. Fireblocks fetches, calculates and caches the result every 30 seconds. Customers should query this API while taking the caching interval into consideration. Notes: - The `networkFee` parameter is the `gasPrice` with a given delta added, multiplied by the gasLimit plus the delta. - The estimation provided depends on the asset type. - For UTXO-based assets, the response contains the `feePerByte` parameter - For ETH-based and all EVM based assets, the response will contain `gasPrice` parameter. This is calculated by adding the `baseFee` to the `actualPriority` based on the latest 12 blocks. The response for ETH-based contains the `baseFee`, `gasPrice`, and `priorityFee` parameters. - For ADA-based assets, the response will contain the parameter `networkFee` and `feePerByte` parameters. - For XRP and XLM, the response will contain the transaction fee. - For other assets, the response will contain the `networkFee` parameter. Learn more about Fireblocks Fee Management in the following [guide](https://developers.fireblocks.com/reference/estimate-transaction-fee). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Estimate transaction fee Source: https://developers.fireblocks.com/api-reference/transactions/estimate-transaction-fee https://swagger.fireblocks.com/openapi.yaml post /transactions/estimate_fee Estimates the transaction fee for a specific transaction request. This endpoint simulates a transaction which means that the system will expect to have the requested asset and balance in the specified wallet. **Note**: Supports all Fireblocks assets except ZCash (ZEC). The PROGRAM_CALL operation is not supported by this endpoint — fee estimation for Solana program calls is not available. The SOROBAN_CALL operation is not supported by this endpoint - fee estimation for Stellar Soroban contract calls is not available. Learn more about Fireblocks Fee Management in the following [guide](https://developers.fireblocks.com/reference/estimate-transaction-fee). Endpoint Permission: Admin, Signer, Approver, Editor. # Freeze a transaction Source: https://developers.fireblocks.com/api-reference/transactions/freeze-a-transaction https://swagger.fireblocks.com/openapi.yaml post /transactions/{txId}/freeze Freezes a transaction by ID. Usually used for AML integrations when the incoming funds should be quarantined. For account based assets - the entire amount of the transaction is frozen For UTXO based assets - all UTXOs of the specified transaction are frozen Endpoint Permission: Admin, Non-Signing Admin. # Get a specific transaction by external transaction ID Source: https://developers.fireblocks.com/api-reference/transactions/get-a-specific-transaction-by-external-transaction-id https://swagger.fireblocks.com/openapi.yaml get /transactions/external_tx_id/{externalTxId} Returns transaction by external transaction ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get a specific transaction by Fireblocks transaction ID Source: https://developers.fireblocks.com/api-reference/transactions/get-a-specific-transaction-by-fireblocks-transaction-id https://swagger.fireblocks.com/openapi.yaml get /transactions/{txId} Get a specific transaction data by Fireblocks Transaction ID Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get transaction history Source: https://developers.fireblocks.com/api-reference/transactions/get-transaction-history https://swagger.fireblocks.com/openapi.yaml get /transactions Get the transaction history for your workspace. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Set confirmation threshold by Fireblocks Transaction ID Source: https://developers.fireblocks.com/api-reference/transactions/set-confirmation-threshold-by-fireblocks-transaction-id https://swagger.fireblocks.com/openapi.yaml post /transactions/{txId}/set_confirmation_threshold Overrides the required number of confirmations for transaction completion Fireblocks Transaction ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Set confirmation threshold by transaction hash Source: https://developers.fireblocks.com/api-reference/transactions/set-confirmation-threshold-by-transaction-hash https://swagger.fireblocks.com/openapi.yaml post /txHash/{txHash}/set_confirmation_threshold Overrides the required number of confirmations for transaction completion by transaction hash. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Unfreeze a transaction Source: https://developers.fireblocks.com/api-reference/transactions/unfreeze-a-transaction https://swagger.fireblocks.com/openapi.yaml post /transactions/{txId}/unfreeze Unfreezes a transaction by Fireblocks Transaction ID and makes the transaction available again. Endpoint Permission: Admin, Non-Signing Admin. # Validate destination address Source: https://developers.fireblocks.com/api-reference/transactions/validate-destination-address https://swagger.fireblocks.com/openapi.yaml get /transactions/validate_address/{assetId}/{address} Checks if an address is valid and active (for XRP, DOT, XLM, and EOS). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Add jsonDidKey to VASP details Source: https://developers.fireblocks.com/api-reference/travel-rule/add-jsondidkey-to-vasp-details https://swagger.fireblocks.com/openapi.yaml put /screening/travel_rule/vasp/update Update VASP Details. Updates a VASP with the provided parameters. Use this endpoint to add your public jsonDIDkey generated by Notabene. # Assign VASP to vault Source: https://developers.fireblocks.com/api-reference/travel-rule/assign-vasp-to-vault https://swagger.fireblocks.com/openapi.yaml post /screening/travel_rule/vault/{vaultAccountId}/vasp Sets the VASP Did for a specific vault. Pass empty string to remove existing one. # Create Trust Network Proof of Address Source: https://developers.fireblocks.com/api-reference/travel-rule/create-trust-network-proof-of-address https://swagger.fireblocks.com/openapi.yaml post /screening/travel_rule/providers/trust/proof_of_address Creates a cryptographic proof of address ownership for TRUST network. # Get All VASPs Source: https://developers.fireblocks.com/api-reference/travel-rule/get-all-vasps https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/vasp Get All VASPs. Returns a list of VASPs. VASPs can be searched and sorted. Each VASP in the response may contain fields that are not documented in the schema below. Clients must ignore unrecognised fields rather than failing to deserialize. # Get assigned VASP to vault Source: https://developers.fireblocks.com/api-reference/travel-rule/get-assigned-vasp-to-vault https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/vault/{vaultAccountId}/vasp Get assigned VASP Did for a specific vault. Returns empty string vaspDid value in response if none assigned. # Get VASP details Source: https://developers.fireblocks.com/api-reference/travel-rule/get-vasp-details https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/vasp/{did} Get VASP Details. Returns information about a VASP that has the specified DID. The response may contain fields that are not documented in the schema below. Clients must ignore unrecognised fields rather than failing to deserialize. # Retrieve Trust Network Proof of Address Signature Source: https://developers.fireblocks.com/api-reference/travel-rule/retrieve-trust-network-proof-of-address-signature https://swagger.fireblocks.com/openapi.yaml get /screening/travel_rule/providers/trust/proof_of_address/{transactionId} Retrieves the TRUST-compatible encoded signature for a proof of address transaction. Send this signature directly to TRUST for verification. # Validate Full Travel Rule Transaction Source: https://developers.fireblocks.com/api-reference/travel-rule/validate-full-travel-rule-transaction https://swagger.fireblocks.com/openapi.yaml post /screening/travel_rule/transaction/validate/full Validate Full Travel Rule transactions. Checks for all required information on the originator and beneficiary VASPs. # Validate Travel Rule Transaction Source: https://developers.fireblocks.com/api-reference/travel-rule/validate-travel-rule-transaction https://swagger.fireblocks.com/openapi.yaml post /screening/travel_rule/transaction/validate Validate Travel Rule transactions. Checks what beneficiary VASP details are required by your jurisdiction and the beneficiary's jurisdiction. **Deprecation Notice** This endpoint will be deprecated soon in favor of the [validate full](https://developers.fireblocks.com/reference/validatefulltravelruletransaction) endpoint. Please update your integrations to use the [validate full](https://developers.fireblocks.com/reference/validatefulltravelruletransaction) endpoint to ensure compatibility with future releases. Checks what beneficiary VASP details are required by your jurisdiction and the beneficiary's jurisdiction. Learn more about Fireblocks Travel Rule management in the following [guide](https://developers.fireblocks.com/docs/define-travel-rule-policies). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Assess Travel Rule requirement Source: https://developers.fireblocks.com/api-reference/trlink/assess-travel-rule-requirement https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/trm/assess Assesses travel rule requirement for a transaction by validating stored credentials and determining whether Travel Rule compliance is required based on amount, jurisdiction, and partner thresholds. # Cancel Travel Rule Message Source: https://developers.fireblocks.com/api-reference/trlink/cancel-travel-rule-message https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/trm/{trmId}/cancel Cancels a travel rule message. The TRM status will be updated to cancelled and the partner will be notified. # Connect customer integration Source: https://developers.fireblocks.com/api-reference/trlink/connect-customer-integration https://swagger.fireblocks.com/openapi.yaml put /screening/trlink/customers/integration/{customerIntegrationId} Connects a customer integration by providing API credentials. Stores encrypted credentials and enables the integration for use. # Create customer Source: https://developers.fireblocks.com/api-reference/trlink/create-customer https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers Creates a new customer (legal entity/VASP) for TRSupport Travel Rule compliance operations. The customer represents your organization in the Travel Rule network and contains IVMS101-compliant identity information. # Create customer integration Source: https://developers.fireblocks.com/api-reference/trlink/create-customer-integration https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration Creates a new TRSupport integration for a customer. This establishes a connection placeholder between a customer and a Travel Rule partner. Use the connect endpoint to provide credentials after creation. You may optionally supply `customerIntegrationId` in the request body when your tenant is enabled for client-provided integration ids. # Create Travel Rule Message Source: https://developers.fireblocks.com/api-reference/trlink/create-travel-rule-message https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/trm Creates a new travel rule message with IVMS101-compliant PII data. Encrypts sensitive originator and beneficiary information before sending to partner. # Delete customer Source: https://developers.fireblocks.com/api-reference/trlink/delete-customer https://swagger.fireblocks.com/openapi.yaml delete /screening/trlink/customers/{customerId} Deletes a customer and all associated integrations. This action cannot be undone. # Disconnect customer integration Source: https://developers.fireblocks.com/api-reference/trlink/disconnect-customer-integration https://swagger.fireblocks.com/openapi.yaml delete /screening/trlink/customers/integration/{customerIntegrationId} Disconnects the integration for the authenticated workspace (tenant): removes stored credentials and deletes this tenant's integration record. The operation is scoped to the caller's tenant; it does not remove partner-side state for other workspaces that reuse the same logical customer integration. The record cannot be recovered after delete. # Get all customers Source: https://developers.fireblocks.com/api-reference/trlink/get-all-customers https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers Retrieves all customers associated with the authenticated tenant. Returns a list of legal entities configured for Travel Rule compliance. # Get customer by ID Source: https://developers.fireblocks.com/api-reference/trlink/get-customer-by-id https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/{customerId} Retrieves detailed information about a specific customer by their unique identifier. # Get customer integration by ID Source: https://developers.fireblocks.com/api-reference/trlink/get-customer-integration-by-id https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/{customerId}/integrations/{customerIntegrationId} Retrieves detailed information about a specific customer integration. # Get customer integrations Source: https://developers.fireblocks.com/api-reference/trlink/get-customer-integrations https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/{customerId}/integrations Retrieves all TRSupport integrations for a specific customer. Returns a list of partner integrations configured for Travel Rule compliance. # Get public key for PII encryption Source: https://developers.fireblocks.com/api-reference/trlink/get-public-key-for-pii-encryption https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/public_key Retrieves the partner's public key in JWK format for encrypting PII data in Travel Rule Messages. Use this key to encrypt sensitive originator and beneficiary information before sending Travel Rule messages. # Get required actions for a TRM Source: https://developers.fireblocks.com/api-reference/trlink/get-required-actions-for-a-trm https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/trm/{trmId}/required_actions Retrieves the list of required actions (e.g., PII fields) needed to process the Travel Rule Message. # Get supported asset by ID Source: https://developers.fireblocks.com/api-reference/trlink/get-supported-asset-by-id https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/assets/{assetId} Retrieves detailed information about a specific asset by its Fireblocks asset ID. Returns the transformed Fireblocks asset data, raw partner response, and support status. # Get TRLink policy Source: https://developers.fireblocks.com/api-reference/trlink/get-trlink-policy https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/policy Retrieves the complete TRSupport policy for the authenticated tenant, including pre-screening rules, post-screening rules, and missing TRM rules. Pre-screening rules determine whether transactions should be screened. Post-screening rules determine actions based on screening results. Missing TRM rules handle cases when screening data is unavailable. # Get TRM by ID Source: https://developers.fireblocks.com/api-reference/trlink/get-trm-by-id https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/trm/{trmId} Retrieves a Travel Rule Message by its unique identifier from the partner provider. Returns full TRM details including status, IVMS101 data, and transaction information. # Get VASP by ID Source: https://developers.fireblocks.com/api-reference/trlink/get-vasp-by-id https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/vasps/{vaspId} Retrieves detailed information about a specific VASP by its unique identifier. Returns VASP details including public key if available. # List available TRSupport partners Source: https://developers.fireblocks.com/api-reference/trlink/list-available-trsupport-partners https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/partners Retrieves a list of all available Travel Rule Support integration partners. Partners provide Travel Rule compliance services such as VASP discovery, TRM exchange, and PII encryption. # List supported assets Source: https://developers.fireblocks.com/api-reference/trlink/list-supported-assets https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/assets Retrieves a paginated list of assets supported by the partner integration. Includes a flag indicating whether the partner can handle assets not explicitly listed. Supports cursor-based pagination. # List VASPs Source: https://developers.fireblocks.com/api-reference/trlink/list-vasps https://swagger.fireblocks.com/openapi.yaml get /screening/trlink/customers/integration/{customerIntegrationId}/vasps Retrieves a paginated list of VASPs (Virtual Asset Service Providers) available through the partner integration. Supports cursor-based pagination. # Manual decision for missing TRM Source: https://developers.fireblocks.com/api-reference/trlink/manual-decision-for-missing-trm https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/transactions/{txId}/manual_decision Accept or reject destinations stuck in NoTRM step without waiting for TRP webhook or policy timeout. # Redirect Travel Rule Message Source: https://developers.fireblocks.com/api-reference/trlink/redirect-travel-rule-message https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/trm/{trmId}/redirect Redirects a Travel Rule Message to a subsidiary VASP. This operation requires the partner to support nested VASPs functionality. # Resolve action for a TRM Source: https://developers.fireblocks.com/api-reference/trlink/resolve-action-for-a-trm https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/trm/{trmId}/resolve_action Submits required data (e.g., beneficiary PII) to resolve a pending Travel Rule Message action. # Set destination travel rule message ID Source: https://developers.fireblocks.com/api-reference/trlink/set-destination-travel-rule-message-id https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/transaction/{txId}/destination/travel_rule_message_id Associates a Travel Rule Message ID with a specific destination in a multi-destination Fireblocks transaction. Matches destinations by amount and peer path. # Set transaction travel rule message ID Source: https://developers.fireblocks.com/api-reference/trlink/set-transaction-travel-rule-message-id https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/transaction/{txId}/travel_rule_message_id Associates a Travel Rule Message ID with a Fireblocks transaction. This links the TRM compliance data to the blockchain transaction. # Test connection Source: https://developers.fireblocks.com/api-reference/trlink/test-connection https://swagger.fireblocks.com/openapi.yaml post /screening/trlink/customers/integration/{customerIntegrationId}/test_connection Tests the connection to a customer integration by validating stored credentials and attempting communication with the Travel Rule partner. Returns connection status and any error messages. # Update customer Source: https://developers.fireblocks.com/api-reference/trlink/update-customer https://swagger.fireblocks.com/openapi.yaml put /screening/trlink/customers/{customerId} Updates an existing customer's information. All fields are optional - only provided fields will be updated. # Create user group Source: https://developers.fireblocks.com/api-reference/user-groups-beta/create-user-group https://swagger.fireblocks.com/openapi.yaml post /management/user_groups Create a new user group. Users with the Viewer role cannot be added to groups. Endpoint Permission: Admin, Non-Signing Admin. # Delete user group Source: https://developers.fireblocks.com/api-reference/user-groups-beta/delete-user-group https://swagger.fireblocks.com/openapi.yaml delete /management/user_groups/{groupId} Delete a user group by ID. **Note**: - This endpoint is now in Beta, disabled for general availability at this time. - Please note that this endpoint is available only for API keys with Admin permissions. # Get user group Source: https://developers.fireblocks.com/api-reference/user-groups-beta/get-user-group https://swagger.fireblocks.com/openapi.yaml get /management/user_groups/{groupId} Get a user group by ID. **Note**: - This endpoint is now in Beta, disabled for general availability at this time. - Please note that this endpoint is available only for API keys with Admin permissions. # List user groups Source: https://developers.fireblocks.com/api-reference/user-groups-beta/list-user-groups https://swagger.fireblocks.com/openapi.yaml get /management/user_groups Get all user groups in your workspace - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Endpoint Permission: Admin, Non-Signing Admin. # Update user group Source: https://developers.fireblocks.com/api-reference/user-groups-beta/update-user-group https://swagger.fireblocks.com/openapi.yaml put /management/user_groups/{groupId} Update a user group by ID. **Note**: - This endpoint is now in Beta, disabled for general availability at this time. - Please note that this endpoint is available only for API keys with Admin permissions. # List users Source: https://developers.fireblocks.com/api-reference/users/list-users https://swagger.fireblocks.com/openapi.yaml get /users List all users for the workspace. Please note that this endpoint is available only for API keys with Admin permissions. # Attach or detach labels to/from UTXOs Source: https://developers.fireblocks.com/api-reference/utxo-management-beta/attach-or-detach-labels-tofrom-utxos https://swagger.fireblocks.com/openapi.yaml patch /utxo_management/{vaultAccountId}/{assetId}/labels Attach or detach labels to/from UTXOs in a vault account. Labels can be used for organizing and filtering UTXOs. Labels are applied additively — `labelsToAttach` adds to the existing label set and `labelsToDetach` removes from it. Neither operation replaces the full set. The request is all-or-nothing: if any identifier cannot be labelled, no UTXO is labelled and the request fails with `400`. The response lists every failed identifier in `failures`, each with its own `reason` — use it, not the status, to decide what to do: - `NOT_FOUND` — not found in this vault and asset. - `NOT_LABELLABLE` — spent, or removed, and can no longer be labelled. A UTXO removed within the last hour is reported as `NOT_FOUND` with `utxoStatus: REMOVED`; if it does not reappear, it becomes `NOT_LABELLABLE` after about an hour. A `400` without `failures` means the request itself is malformed. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Get UTXO selection config Source: https://developers.fireblocks.com/api-reference/utxo-management-beta/get-utxo-selection-config https://swagger.fireblocks.com/openapi.yaml get /utxo_management/selection_config Returns the workspace-level configured selection strategy and the effective strategy after runtime resolution. `ADAPTIVE` is the recommended strategy. When no row is stored (source `DEFAULT`), `effective` is `ADAPTIVE` if adaptive selection is serving for this workspace, otherwise `ASC`. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get vault and asset UTXO selection config Source: https://developers.fireblocks.com/api-reference/utxo-management-beta/get-vault-and-asset-utxo-selection-config https://swagger.fireblocks.com/openapi.yaml get /utxo_management/{vaultAccountId}/{assetId}/selection_config Returns the config stored at this vault-and-asset scope, if any, and the effective strategy after workspace fallback and runtime resolution. `ADAPTIVE` is the recommended strategy. When no row is stored at this scope and none is inherited from the workspace (source `DEFAULT`), `effective` is `ADAPTIVE` if adaptive selection is serving for this scope, otherwise `ASC`. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # List unspent outputs (UTXOs) Source: https://developers.fireblocks.com/api-reference/utxo-management-beta/list-unspent-outputs-utxos https://swagger.fireblocks.com/openapi.yaml get /utxo_management/{vaultAccountId}/{assetId}/unspent_outputs Returns a paginated list of unspent transaction outputs (UTXOs) for a UTXO-based asset in a vault account, with optional filters for labels, statuses, amounts, and more. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Upsert UTXO selection config Source: https://developers.fireblocks.com/api-reference/utxo-management-beta/upsert-utxo-selection-config https://swagger.fireblocks.com/openapi.yaml put /utxo_management/selection_config Creates or updates the workspace-level UTXO selection strategy. `ADAPTIVE` is recommended. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin. # Upsert vault and asset UTXO selection config Source: https://developers.fireblocks.com/api-reference/utxo-management-beta/upsert-vault-and-asset-utxo-selection-config https://swagger.fireblocks.com/openapi.yaml put /utxo_management/{vaultAccountId}/{assetId}/selection_config Creates or updates the UTXO selection strategy for this vault account and asset. `ADAPTIVE` is recommended. **Note:** These endpoints are currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin. # Activate a USDC Gateway wallet Source: https://developers.fireblocks.com/api-reference/vaults/activate-a-usdc-gateway-wallet https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/usdc_gateway/activate Activates the USDC Gateway wallet associated with the given vault account. If the wallet does not yet exist it is created in an activated state. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver. # Activate a wallet in a vault account Source: https://developers.fireblocks.com/api-reference/vaults/activate-a-wallet-in-a-vault-account https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/{assetId}/activate Initiates activation for a wallet in a vault account. Activation is required for tokens that need an on-chain transaction for creation. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Assign AML customer reference ID Source: https://developers.fireblocks.com/api-reference/vaults/assign-aml-customer-reference-id https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/{assetId}/addresses/{addressId}/set_customer_ref_id Sets an AML/KYT customer reference ID for a specific address. Endpoint Permission: Admin, Non-Signing Admin. # Attach or detach tags from vault accounts Source: https://developers.fireblocks.com/api-reference/vaults/attach-or-detach-tags-from-vault-accounts https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/attached_tags Attach or detach one or more tags from the requested vault accounts. Endpoint Permission: For protected tags: Owner, Admin, Non-Signing Admin. For non protected tags: Owner, Admin, Non-Signing Admin, Signer, Editor, Approver. # Bulk creation of new deposit addresses Source: https://developers.fireblocks.com/api-reference/vaults/bulk-creation-of-new-deposit-addresses https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/addresses/bulk **For UTXO blockchains only.** Create multiple deposit addresses by running an async job. - The target Vault account should already have a UTXO asset wallet with a permanent address. - Limited to a maximum of 10,000 addresses per operation. Use multiple operations for the same Vault account/permanent address if needed. **Endpoint Permissions:** Admin, Non-Signing Admin. # Bulk creation of new vault accounts Source: https://developers.fireblocks.com/api-reference/vaults/bulk-creation-of-new-vault-accounts https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/bulk Create multiple vault accounts by running an async job. - The HBAR, TON, SUI, TERRA, ALGO, and DOT blockchains are not supported. - These endpoints are currently in beta and might be subject to changes. - Limited to a maximum of 10,000 accounts per operation. - With `vaultType: KEY_LINK`, creates Key Link vault accounts; set `autoAssign: true` to bind them to the workspace's signing keys (see `vaultType` and `autoAssign`). **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor. # Change a USDC Gateway deposit automation Source: https://developers.fireblocks.com/api-reference/vaults/change-a-usdc-gateway-deposit-automation https://swagger.fireblocks.com/openapi.yaml patch /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation/{automationId} Changes an existing USDC Gateway deposit automation for a vault account. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver. # Convert a segwit address to legacy format Source: https://developers.fireblocks.com/api-reference/vaults/convert-a-segwit-address-to-legacy-format https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/{assetId}/addresses/{addressId}/create_legacy Converts an existing segwit address to the legacy format. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create a new vault account Source: https://developers.fireblocks.com/api-reference/vaults/create-a-new-vault-account https://swagger.fireblocks.com/openapi.yaml post /vault/accounts Creates a new vault account with the requested name. **Note: ** Vault account names should consist of ASCII characters only. Learn more about Fireblocks Vault Accounts in the following [guide](https://developers.fireblocks.com/reference/create-vault-account). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create a new vault wallet Source: https://developers.fireblocks.com/api-reference/vaults/create-a-new-vault-wallet https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/{assetId} Creates a wallet for a specific asset in a vault account. Learn more about Fireblocks Vault Wallets in the following [guide](https://developers.fireblocks.com/reference/create-vault-wallet). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create new asset deposit address Source: https://developers.fireblocks.com/api-reference/vaults/create-new-asset-deposit-address https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/{assetId}/addresses Creates a new deposit address for an asset of a vault account. Should be used for UTXO or Tag/Memo based assets ONLY. Requests with account based assets will fail. Endpoint Permission: Admin, Non-Signing Admin. # Deactivate a USDC Gateway wallet Source: https://developers.fireblocks.com/api-reference/vaults/deactivate-a-usdc-gateway-wallet https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/usdc_gateway/deactivate Deactivates the USDC Gateway wallet associated with the given vault account. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver. # Get a vault account by ID Source: https://developers.fireblocks.com/api-reference/vaults/get-a-vault-account-by-id https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId} Get a vault account by its unique ID. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get addresses (Paginated) Source: https://developers.fireblocks.com/api-reference/vaults/get-addresses-paginated https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId}/addresses_paginated Returns a paginated response of the addresses for a given vault account and asset. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get an asset's public key Source: https://developers.fireblocks.com/api-reference/vaults/get-an-assets-public-key https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId}/{change}/{addressIndex}/public_key_info Get the public key information for a specific asset in a vault account. Endpoint Permission: Admin, Non-Signing Admin. # Get asset addresses Source: https://developers.fireblocks.com/api-reference/vaults/get-asset-addresses https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId}/addresses DEPRECATED! - If your application logic or scripts rely on the deprecated endpoint, you should update to account for GET/V1/vault/accounts/{vaultAccountId}/{assetId}/addresses_paginated before Mar 31,2024. - All workspaces created after Mar 31,2024. will have it disabled. If it is disabled for your workspace and you attempt to use it, you will receive the following error message: "This endpoint is unavailable. - Please use the GET /v1/vault/accounts/{vaultAccountId}/{assetId}/addresses_paginated endpoint to return all the wallet addresses associated with the specified vault account and asset in a paginated list. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get asset balance for chosen assets Source: https://developers.fireblocks.com/api-reference/vaults/get-asset-balance-for-chosen-assets https://swagger.fireblocks.com/openapi.yaml get /vault/assets Gets the assets amount summary for all accounts or filtered accounts. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get job status of bulk creation of new vault accounts Source: https://developers.fireblocks.com/api-reference/vaults/get-job-status-of-bulk-creation-of-new-vault-accounts https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/bulk/{jobId} Returns the current status of (or error for) the specified vault account bulk creation job. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get max spendable amount in a transaction Source: https://developers.fireblocks.com/api-reference/vaults/get-max-spendable-amount-in-a-transaction https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId}/max_spendable_amount **UTXO assets only.** Retrieve the maximum amount of the specified asset that can be spent in a single transaction from the specified vault account. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get maximum BIP44 index used Source: https://developers.fireblocks.com/api-reference/vaults/get-maximum-bip44-index-used https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId}/max_bip44_index_used Retrieves the maximum BIP44 address index and change address index used for a specific asset in a vault account (BIP44 standard). # Get the asset balance for a vault account Source: https://developers.fireblocks.com/api-reference/vaults/get-the-asset-balance-for-a-vault-account https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId} Returns a specific vault wallet balance information for a specific asset. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get the job status of the bulk deposit address creation Source: https://developers.fireblocks.com/api-reference/vaults/get-the-job-status-of-the-bulk-deposit-address-creation https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/addresses/bulk/{jobId} Returns the current status of (or an error for) the specified deposit addresss bulk creation job. **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor, and Viewer. # Get the public key for a derivation path Source: https://developers.fireblocks.com/api-reference/vaults/get-the-public-key-for-a-derivation-path https://swagger.fireblocks.com/openapi.yaml get /vault/public_key_info Gets the public key information based on derivation path and signing algorithm. Endpoint Permission: Admin, Non-Signing Admin. # Get USDC Gateway wallet info Source: https://developers.fireblocks.com/api-reference/vaults/get-usdc-gateway-wallet-info https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/usdc_gateway Returns the USDC Gateway wallet information associated with the given vault account. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get UTXO unspent inputs information Source: https://developers.fireblocks.com/api-reference/vaults/get-utxo-unspent-inputs-information https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/{assetId}/unspent_inputs Returns unspent inputs information of an UTXO asset in a vault account. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get vault accounts Source: https://developers.fireblocks.com/api-reference/vaults/get-vault-accounts https://swagger.fireblocks.com/openapi.yaml get /vault/accounts DEPRECATED - Please use `/vault/accounts_paged` endpoint instead. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get vault accounts (Paginated) Source: https://developers.fireblocks.com/api-reference/vaults/get-vault-accounts-paginated https://swagger.fireblocks.com/openapi.yaml get /vault/accounts_paged Gets all vault accounts in your workspace. This endpoint returns a limited amount of results with a quick response time. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get vault balance by an asset Source: https://developers.fireblocks.com/api-reference/vaults/get-vault-balance-by-an-asset https://swagger.fireblocks.com/openapi.yaml get /vault/assets/{assetId} Get the total balance of an asset across all the vault accounts. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Get vault wallets (Paginated) Source: https://developers.fireblocks.com/api-reference/vaults/get-vault-wallets-paginated https://swagger.fireblocks.com/openapi.yaml get /vault/asset_wallets Get all vault wallets of the vault accounts in your workspace. A vault wallet is an asset in a vault account. This method allows fast traversal of all account balances. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Hide a vault account in the console Source: https://developers.fireblocks.com/api-reference/vaults/hide-a-vault-account-in-the-console https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/hide Hides the requested vault account from the web console view. This operation is required when creating thousands of vault accounts to serve your end-users. Used for preventing the web console to be swamped with too much vault accounts. Learn more in the following [guide](https://developers.fireblocks.com/docs/create-direct-custody-wallets#hiding-vault-accounts). NOTE: Hiding the vault account from the web console will also hide all the related transactions to/from this vault. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Look up a vault account by blockchain address Source: https://developers.fireblocks.com/api-reference/vaults/look-up-a-vault-account-by-blockchain-address https://swagger.fireblocks.com/openapi.yaml get /vault/lookup_by_address Resolves a blockchain address to the vault account that owns it. Returns the vault account ID and the blockchains associated with the address. **Note:** This endpoint is currently in beta and might be subject to changes. # Read the USDC Gateway deposit automations for a vault account Source: https://developers.fireblocks.com/api-reference/vaults/read-the-usdc-gateway-deposit-automations-for-a-vault-account https://swagger.fireblocks.com/openapi.yaml get /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation Returns the USDC Gateway deposit automations configured for the given vault account. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor, Viewer. # Refresh asset balance data Source: https://developers.fireblocks.com/api-reference/vaults/refresh-asset-balance-data https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/{assetId}/balance Updates the balance of a specific asset in a vault account. This API endpoint is subject to a strict rate limit. Should be used by clients in very specific scenarios. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Rename a vault account Source: https://developers.fireblocks.com/api-reference/vaults/rename-a-vault-account https://swagger.fireblocks.com/openapi.yaml put /vault/accounts/{vaultAccountId} Renames the requested vault account. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver. # Set an AML/KYT ID for a vault account Source: https://developers.fireblocks.com/api-reference/vaults/set-an-amlkyt-id-for-a-vault-account https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/set_customer_ref_id Assigns an AML/KYT customer reference ID for the vault account. Learn more about Fireblocks AML management in the following [guide](https://developers.fireblocks.com/docs/define-aml-policies). Endpoint Permission: Admin, Non-Signing Admin. # Set auto fueling to on or off Source: https://developers.fireblocks.com/api-reference/vaults/set-auto-fueling-to-on-or-off https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/set_auto_fuel Toggles the auto fueling property of the vault account to enabled or disabled. Vault Accounts with 'autoFuel=true' are monitored and auto fueled by the Fireblocks Gas Station. Learn more about the Fireblocks Gas Station in the following [guide](https://developers.fireblocks.com/docs/work-with-gas-station). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Set up a USDC Gateway deposit automation for a vault account Source: https://developers.fireblocks.com/api-reference/vaults/set-up-a-usdc-gateway-deposit-automation-for-a-vault-account https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation Turns on automatic deposits into the USDC Gateway wallet for the given vault account, on the schedule you choose. Returns an error if an automation already exists for this vault account and asset. Use PATCH to change it instead. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver. # Stop a USDC Gateway deposit automation's schedule Source: https://developers.fireblocks.com/api-reference/vaults/stop-a-usdc-gateway-deposit-automations-schedule https://swagger.fireblocks.com/openapi.yaml delete /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation/{automationId} Stops the schedule for an existing deposit automation. The automation itself stays configured, only its schedule stops. Turn it back on later with PATCH, without setting up the automation again from scratch. **Note:** This endpoint is currently in beta and might be subject to changes. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver. # Unhide a vault account in the console Source: https://developers.fireblocks.com/api-reference/vaults/unhide-a-vault-account-in-the-console https://swagger.fireblocks.com/openapi.yaml post /vault/accounts/{vaultAccountId}/unhide Makes a hidden vault account visible in web console view. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Update address description Source: https://developers.fireblocks.com/api-reference/vaults/update-address-description https://swagger.fireblocks.com/openapi.yaml put /vault/accounts/{vaultAccountId}/{assetId}/addresses/{addressId} Updates the description of an existing address of an asset in a vault account. Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Create a new Web3 connection. Source: https://developers.fireblocks.com/api-reference/web3-connections/create-a-new-web3-connection https://swagger.fireblocks.com/openapi.yaml post /connections/wc Initiate a new Web3 connection. * Note: After this succeeds, make a request to `PUT /v1/connections/wc/{id}` (below) to approve or reject the new Web3 connection. # List all open Web3 connections. Source: https://developers.fireblocks.com/api-reference/web3-connections/list-all-open-web3-connections https://swagger.fireblocks.com/openapi.yaml get /connections Get open Web3 connections. # Remove an existing Web3 connection. Source: https://developers.fireblocks.com/api-reference/web3-connections/remove-an-existing-web3-connection https://swagger.fireblocks.com/openapi.yaml delete /connections/wc/{id} Remove a Web3 connection # Respond to a pending Web3 connection request. Source: https://developers.fireblocks.com/api-reference/web3-connections/respond-to-a-pending-web3-connection-request https://swagger.fireblocks.com/openapi.yaml put /connections/wc/{id} Submit a response to *approve* or *reject* an initiated Web3 connection. * Note: This call is used to complete your `POST /v1/connections/wc/` request. After this succeeds, your new Web3 connection is created and functioning. # Create a new webhook Source: https://developers.fireblocks.com/api-reference/webhooks-v2/create-a-new-webhook https://swagger.fireblocks.com/openapi.yaml post /webhooks Creates a new webhook, which will be triggered on the specified events **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Create an mTLS configuration Source: https://developers.fireblocks.com/api-reference/webhooks-v2/create-an-mtls-configuration https://swagger.fireblocks.com/openapi.yaml post /webhooks_settings/mtls Stores a certificate signed against the CSR from `GET /v1/webhooks_settings/mtls_csr` and returns its id, which is then set as `webhookMtlsId` on a webhook or on OAuth credentials. The private key the certificate was issued for is derived from the certificate, so it is never named by the caller. Re-uploading a certificate already stored returns the existing id rather than creating a second configuration, so several webhooks and OAuth credentials can share one certificate. A certificate that was not issued for a private key this workspace holds is rejected with a `400`. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Create OAuth credentials Source: https://developers.fireblocks.com/api-reference/webhooks-v2/create-oauth-credentials https://swagger.fireblocks.com/openapi.yaml post /webhooks_settings/oauth Creates a reusable OAuth client credential set. Attach it to a webhook by passing the returned id as that webhook's `webhookOauthId`. Several webhooks may share one credential set, so rotating its client secret covers all of them at once. The client secret is write-only and is never returned. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Delete an mTLS configuration Source: https://developers.fireblocks.com/api-reference/webhooks-v2/delete-an-mtls-configuration https://swagger.fireblocks.com/openapi.yaml delete /webhooks_settings/mtls/{webhookMtlsId} Deletes an mTLS configuration. By default the delete is refused while the configuration is still in use: if any webhook or OAuth credentials reference it, nothing is deleted and the request fails with `409 Conflict`, naming the reason and listing the ids of what references it. This protects a shared configuration from being removed out from under the webhooks and token requests that depend on it. Pass `forceDelete=true` to delete anyway. That detaches everything referencing it — it clears `webhookMtlsId` on each webhook and OAuth credentials, it does **not** delete them — then deletes the configuration and returns the deleted resource together with `detachedWebhookIds` and `detachedWebhookOauthIds`. Detached webhooks keep delivering notifications, and detached OAuth credentials keep requesting tokens, but without a client certificate, so an endpoint that requires mTLS will reject them from that point on. When nothing references the configuration the delete succeeds either way, and both lists come back empty. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Delete OAuth credentials Source: https://developers.fireblocks.com/api-reference/webhooks-v2/delete-oauth-credentials https://swagger.fireblocks.com/openapi.yaml delete /webhooks_settings/oauth/{webhookOauthId} Deletes an OAuth credential set. By default the delete is refused while the credentials are still in use: if any webhook references them, nothing is deleted and the request fails with `409 Conflict`, naming the reason and listing the ids of the referencing webhooks. This protects a shared credential set from being removed out from under the webhooks that depend on it, since several webhooks may reference the same one. Pass `forceDelete=true` to delete anyway. That detaches every referencing webhook — it clears each webhook's `webhookOauthId`, it does **not** delete the webhook — then deletes the credential set and returns the deleted resource together with `detachedWebhookIds`. The detached webhooks keep delivering notifications, but without an `Authorization` header, so their endpoints will see unauthenticated deliveries from that point on. When nothing references the credentials the delete succeeds either way, and `detachedWebhookIds` comes back empty. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Delete webhook Source: https://developers.fireblocks.com/api-reference/webhooks-v2/delete-webhook https://swagger.fireblocks.com/openapi.yaml delete /webhooks/{webhookId} Delete a webhook by its id Endpoint Permission: Owner, Admin, Non-Signing Admin. # Get all notifications by webhook id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-all-notifications-by-webhook-id https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId}/notifications Get all notifications by webhook id (paginated) # Get all OAuth credentials Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-all-oauth-credentials https://swagger.fireblocks.com/openapi.yaml get /webhooks_settings/oauth Lists every OAuth credential set for the workspace. Client secrets are never returned. # Get all webhooks Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-all-webhooks https://swagger.fireblocks.com/openapi.yaml get /webhooks Get all webhooks (paginated). # Get an mTLS configuration by id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-an-mtls-configuration-by-id https://swagger.fireblocks.com/openapi.yaml get /webhooks_settings/mtls/{webhookMtlsId} Retrieve one stored mTLS configuration by its id. # Get mTLS CSR Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-mtls-csr https://swagger.fireblocks.com/openapi.yaml get /webhooks_settings/mtls_csr Returns the Certificate Signing Request (CSR) PEM that customers use to generate their signed client certificate. The private key the CSR is built from is held by Fireblocks and is specific to this workspace. It is created on the first request for a given key type, and the same CSR is returned on subsequent requests for that type. Pass `keyAlgorithm` to choose RSA or ECDSA. A workspace may hold one key of each: the CSR returned is always the one for the type requested, so a certificate signed against it matches the key used at delivery time. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Get notification attempts Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-notification-attempts https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId}/notifications/{notificationId}/attempts Get notification attempts by notification id # Get notification by id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-notification-by-id https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId}/notifications/{notificationId} Get notification by id # Get OAuth credentials by id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-oauth-credentials-by-id https://swagger.fireblocks.com/openapi.yaml get /webhooks_settings/oauth/{webhookOauthId} Retrieve an OAuth credential set by its id. The client secret is never returned. # Get resend by query job status Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-resend-by-query-job-status https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId}/notifications/resend_by_query/jobs/{jobId} Get the status of a resend by query job # Get resend job status Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-resend-job-status https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId}/notifications/resend_failed/jobs/{jobId} Get the status of a resend job # Get webhook by id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-webhook-by-id https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId} Retrieve a webhook by its id # Get webhook metrics Source: https://developers.fireblocks.com/api-reference/webhooks-v2/get-webhook-metrics https://swagger.fireblocks.com/openapi.yaml get /webhooks/{webhookId}/metrics/{metricName} Get webhook metrics by webhook id and metric name # List the uploaded mTLS configurations Source: https://developers.fireblocks.com/api-reference/webhooks-v2/list-the-uploaded-mtls-configurations https://swagger.fireblocks.com/openapi.yaml get /webhooks_settings/mtls Lists the workspace's mTLS configurations, newest first. Pass `ids` to ask about particular ones instead — useful for resolving the `webhookMtlsId` values on a set of webhooks in one call. # Resend failed notifications Source: https://developers.fireblocks.com/api-reference/webhooks-v2/resend-failed-notifications https://swagger.fireblocks.com/openapi.yaml post /webhooks/{webhookId}/notifications/resend_failed Resend all failed notifications for a webhook in the last 24 hours Endpoint Permission: Owner, Admin, Non-Signing Admin, Editor, Signer. # Resend notification by id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/resend-notification-by-id https://swagger.fireblocks.com/openapi.yaml post /webhooks/{webhookId}/notifications/{notificationId}/resend Resend notification by ID Endpoint Permission: Owner, Admin, Non-Signing Admin, Editor, Signer. # Resend notifications by query Source: https://developers.fireblocks.com/api-reference/webhooks-v2/resend-notifications-by-query https://swagger.fireblocks.com/openapi.yaml post /webhooks/{webhookId}/notifications/resend_by_query Resend notifications matching the given query filters (statuses, events, time range, resource ID) Endpoint Permission: Owner, Admin, Non-Signing Admin, Editor, Signer. # Resend notifications by resource Id Source: https://developers.fireblocks.com/api-reference/webhooks-v2/resend-notifications-by-resource-id https://swagger.fireblocks.com/openapi.yaml post /webhooks/{webhookId}/notifications/resend_by_resource Resend notifications by resource Id Endpoint Permission: Owner, Admin, Non-Signing Admin, Editor, Signer. # Update an mTLS configuration Source: https://developers.fireblocks.com/api-reference/webhooks-v2/update-an-mtls-configuration https://swagger.fireblocks.com/openapi.yaml patch /webhooks_settings/mtls/{webhookMtlsId} Renames a configuration, replaces its certificate, or both. Only the fields present in the request are changed; anything omitted is left as it is, and a request with neither field is rejected with a `400`. Replacing `signedCert` switches every webhook and OAuth credentials set using this configuration over to the new certificate in one write, and the private key it was issued for is re-derived from the certificate. A replacement that was not issued for a private key this workspace holds is rejected with a `400`. Sending `name: null` removes the label. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Update OAuth credentials Source: https://developers.fireblocks.com/api-reference/webhooks-v2/update-oauth-credentials https://swagger.fireblocks.com/openapi.yaml patch /webhooks_settings/oauth/{webhookOauthId} Updates only the fields present in the request; anything omitted is left as it is. Sending `clientSecret` on its own rotates the secret for every webhook using these credentials. `customJwtClaims`, `customBodyParams` and `customHeaders` are all merged key by key rather than replaced, the same way a webhook's own `customHeaders` behaves: a key sent with a value is added or overwritten, a key sent with a `null` value is deleted, and a key you omit is left alone. Setting one of the three to `null` as a whole clears that map, which is the quick way to empty it without naming every key. There is no ambiguity between the two uses of `null` — one names an entry to delete, the other names the field. A claim cannot be set to JSON `null`, though, on this endpoint or on create, because `null` is spent on deletion. `webhookMtlsId` is a scalar rather than a map, so `null` there does remove it. **Endpoint Permissions:** Owner, Admin, Non-Signing Admin. # Update webhook Source: https://developers.fireblocks.com/api-reference/webhooks-v2/update-webhook https://swagger.fireblocks.com/openapi.yaml patch /webhooks/{webhookId} Update a webhook by its id Endpoint Permission: Owner, Admin, Non-Signing Admin. # Resend failed webhooks Source: https://developers.fireblocks.com/api-reference/webhooks/resend-failed-webhooks https://swagger.fireblocks.com/openapi.yaml post /webhooks/resend Resends all failed webhook notifications. Learn more about Fireblocks Webhooks in the following [guide](https://developers.fireblocks.com/docs/configure-webhooks). Endpoint Permission: Admin, Non-Signing Admin, Signer, Approver, Editor. # Resend webhooks for a transaction by ID Source: https://developers.fireblocks.com/api-reference/webhooks/resend-webhooks-for-a-transaction-by-id https://swagger.fireblocks.com/openapi.yaml post /webhooks/resend/{txId} Resends webhook notifications for a transaction by its unique identifier. Learn more about Fireblocks Webhooks in the following [guide](https://developers.fireblocks.com/docs/configure-webhooks). **Endpoint Permissions:** Admin, Non-Signing Admin, Signer, Approver, Editor. # Get whitelisted ip addresses for an API Key Source: https://developers.fireblocks.com/api-reference/whitelist-ip-addresses/get-whitelisted-ip-addresses-for-an-api-key https://swagger.fireblocks.com/openapi.yaml get /management/api_users/{userId}/whitelist_ip_addresses Get a list of the whitelisted IP addresses for a specific API Key - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Endpoint Permission: Admin, Non-Signing Admin. # Returns current workspace status Source: https://developers.fireblocks.com/api-reference/workspace-status-beta/returns-current-workspace-status https://swagger.fireblocks.com/openapi.yaml get /management/workspace_status Returns current workspace status (Beta). **Note**: - This endpoint is now in Beta, disabled for general availability at this time. - Please note that this endpoint is available only for API keys with Admin/Non Signing Admin permissions. Endpoint Permission: Admin, Non-Signing Admin. # Freeze Source: https://developers.fireblocks.com/api-reference/workspace/freeze https://swagger.fireblocks.com/openapi.yaml post /workspace/freeze Freezes a Workspace so that ALL operations by ANY user are blocked. You should only perform this action when the workspace faces imminent risk, such as when you have a security breach. To unfreeze a workspace, the workspace Owner must submit a request to Fireblocks Support. **NOTE:** - This operation can only be performed by the workspace Admins - Your workspace continues to receive incoming transfers during this time. Endpoint Permission: Admin, Non-Signing Admin. # Get workspace Source: https://developers.fireblocks.com/api-reference/workspace/get-workspace https://swagger.fireblocks.com/openapi.yaml get /workspace Returns the workspace ID and name for the authenticated user. # Changelog Source: https://developers.fireblocks.com/changelog [Fireblocks Changelog](https://changelog.fireblocks.com/) # Install SGX Alibaba Cloud API Co-signer Source: https://developers.fireblocks.com/cosigners/alibaba/install-api-cosigner-alibaba ## Overview To install an SGX Co-signer in Alibaba Cloud and connect it to your workspace, follow these steps: 1. **Setup and configure your Alibaba Cloud environment** Prepare your Alibaba Cloud environment by creating and configuring the required resources. Ensure it meets the necessary specifications and security settings. 2. **Add a Co-signer to the workspace using an API user** Using the Fireblocks Console or APIs, create an API user and use it to add a Co-signer to the workspace. 3. **Install and connect the Co-signer to the workspace** Download the installation script to the SGX-capable virtual machine and run the script to install the Co-signer. Once installation is complete, the workspace owner approves the new MPC key shares for the API user through the Fireblocks mobile app. You can now view the Co-signer and its paired API user in your Fireblocks Console. Additionally, you can retrieve information about them using the Co-signer APIs. *** ## Step 1: Setup and configure your Alibaba Cloud environment ### 1.1. Allowlist domains To ensure the Co-signer can be installed and operated successfully, add the following domains to your allowlist: | Domain | Owner | | - | - | | `mobile-api.fireblocks.io` | Fireblocks | | `signurl.fireblocks.io` | Fireblocks | | `s3signurl.fireblocks.io` | Fireblocks | | `fb-certs.s3.amazonaws.com` | AWS | | `fb-customers.s3.amazonaws.com/uploads` | AWS | | `fb-cosigner-images.s3.amazonaws.com` | AWS | | `fb-customers.s3.amazonaws.com` | AWS | | `download.docker.com` | Docker | | `registry.gitlab.com` | GitLab | | `cdn.registry.gitlab-static.net` | GitLab | | `gitlab.com` | GitLab | | `github.com` | GitHub | | `download.01.org` | Intel SGX Driver | | `bootstrap.pypa.io` | Python Software Foundation | | `files.pythonhosted.org` | Python Software Foundation | | `pypi.org` | Python Software Foundation | | `pypi.python.org` | Python Software Foundation | Fireblocks-owned domains differ based on the specific Fireblocks SaaS environment you are connected to. If you are connected to the European or Swiss SaaS, update your allowlist according to the domains listed in the table below: | Fireblocks SaaS | Domains to Allow | | - | - | | Global | `mobile-api.fireblocks.io` `signurl.fireblocks.io` `s3signurl.fireblocks.io` | | Europe | `eu2-mobile-api.fireblocks.io` `eu2-signurl.fireblocks.io` `eu2-s3signurl.fireblocks.io` | | Swiss | `eu-mobile-api.fireblocks.io` `eu-signurl.fireblocks.io` `eu-s3signurl.fireblocks.io` | Additionally, ensure port access is configured for the following services: | Port | Service URL | | - | - | | 443 | `https://mobile-api.fireblocks.io` | | 443 | `https://s3signurl.fireblocks.io` | | 443 | `https://fb-certs.s3.amazonaws.com` | | 443 | `https://fb-customers.s3.amazonaws.com/uploads/` | | 443 | `https://bootstrap.pypa.io/get-pip.py` | | 443 | `https://download.docker.com/linux` | | 443 | `https://download.01.org/intel-sgx/` | | 5000 | `https://registry.gitlab.com/customer-cosigner` | > [!NOTE] > > If you cannot support DNS IP whitelisting with an advanced firewall or within the cloud itself, you must allow egress traffic to any destination on port 443. ### 1.2. Create an SGX virtual machine > [!NOTE] > > See this [Alibaba Security Enhanced Instance Family Overview](https://www.alibabacloud.com/help/en/elastic-compute-service/latest/security-enhanced-instance-family-overview) document for details on which instance type to select. **The minimum hardware requirements for the VM are:** * RAM: 16GB * Storage: 256GB * OS: * Ubuntu 22.04 LTS, 24.04 LTS, or 26.04 LTS (Canonical). Ubuntu 26.04 LTS requires API Co-signer version 2026.10.21 or later. * Latest Linux kernel version * Latest Intel microcode (BIOS update) Complete the following steps to create an SGX-capable VM in Alibaba Cloud: 1. On the **Workbench** page, click **Elastic Compute Service**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/15c2003572e6d9eeabed54ce27c1cb92bbca3190db573764d6fb13d2baeaaef5-Screenshot_2024-12-06_150420.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=a980289f63b5f22d86c9e387e4853e31) 2. On the Overview tab, click **Create Instance**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/8f4e009835a35446185928f0c2fd514056dfbd06d6e51d8272c8b566b552c268-Screenshot_2024-12-06_150445.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=cc878fdbbc4089fce0553f0226cb8928) 3. Select the following options from Custom Launch: 1. Region: Select your region. 2. Instance Type: Enter "g7t" in "Search by instance type name", then select `ecs.g7t.2xlarge` (recommended). ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/34764b8f91700560b9be7bae8657747d054465baf068f0c3a4c059033c701b17-Screenshot_2024-12-06_150515.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=a865d1b6cde4102a0f0dfdd68f1c8c9a) 4. Complete the Networking, System Configurations, and Grouping pages per your organization's policies for each. 5. Click **Create Instance**. ### 1.3. Verify SGX is enabled on your VM After creating your virtual machine, confirm that SGX is enabled. The SGX Co-signer requires a server with SGX enabled and the latest patches applied. This verification ensures smooth operation and avoids potential issues. To verify that SGX is enabled on the VM, run the following commands with root privileges:: ```bash theme={"system"} apt update apt upgrade apt install cpuid cpuid -1 | grep -i sgx ``` Verify the following: 1. `SGX`: Software Guard Extensions supported is **true** 2. `SGX_LC`: SGX launch config supported is **true** ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/fe23ee1939feee21b46aa47061c3d01ab28840989133235e3d3b578d3722630a-Screenshot_2024-12-06_133814.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=79db17bab8bfbeff948a5374cf9a8363) ### 1.4. Additional security recommendations It is highly recommended to control user and network access to Co-signer's machine. See [API Co-signer security checklist and recommended defense and monitoring systems](/cosigners/overview/co-signer-security-checklist-defense-monitoring) for further information. *** ## Step 2: Add a Co-signer to the workspace using an API user Follow the instructions to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Ensure you copy to your clipboard the following items, which you will use during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script *** ## Step 3: Install and connect the Co-signer to the workspace > [!NOTE] > > You must have root privileges on the Co-signer machine to install the Co-signer. Ensure you are logged in as a root user or use `sudo` to execute the commands. ### 3.1. Download the installation script Using the download link of the SGX Co-signer installation script you copied from the Console, run the `curl` command to download the package directly to your machine. You can find the download link in the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). Paste the appropriate URL into the following command: ```bash theme={"system"} curl -o cosigner "URL" ``` ### 3.2. Run the installation script After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` To install the Co-signer, run: ```bash theme={"system"} ./cosigner setup ``` You will be prompted to enter the **Pairing token** for the API user, which you retrieve from the Fireblocks Console. This token pairs the API user with the Co-signer. At this stage, you will have the option to configure the Callback Handler parameters for the API user connecting the Co-signer to the workspace. This feature is optional. You can [configure it later through the Console, APIs, or locally](/cosigners/operations/api-cosigner-operate) from the Co-signer's host machine. For detailed instructions on setting up your Callback Handler's interface to the Co-signer and implement its logic and code, refer to the [Setup API Co-signer Callback Handler](/reference/api-cosigner-setup-callback-handler) section. The setup process validates the machine's hardware and installs the necessary drivers to support the appropriate SGX version and the SGX Co-signer's executable image. It includes an attestation flow to ensure that the SGX Co-signer's executable runs securely inside an Intel SGX enclave. Additionally, the script installs other required components. Once the installation is complete, the Co-signer will automatically start running. At the end of the process, the Co-Signer generates a JSON configuration file, which can be used for future configuration updates. ### 3.3: Approve MPC key shares for the API user If the API user used to pair with the Co-Signer and connect it to your workspace has an Admin or Signer role, the workspace owner will receive a notification. This notification will prompt them to approve a new MPC key share request for that API user using the Fireblocks mobile app. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. Observe it is online and that the API user is paired to it. *** > [!NOTE] > > To check the Co-signer's status and observe the logs, see the SGX Co-signer Maintenance article. # Intel SGX Co-signer Architecture Source: https://developers.fireblocks.com/cosigners/alibaba/sgx-architecture-alibaba > [!NOTE] > > Learn how to install SGX Co-signer in Alibaba Cloud in the [following guide](/cosigners/alibaba/install-api-cosigner-alibaba). ## Resources used by the SGX Co-signer The Fireblocks SGX Co-signer utilizes Intel's SGX enclave and attestation mechanisms. It can be deployed either in cloud service providers that support compatible SGX servers or on-premise using a bare-metal server. Also, an SGX driver must be loaded into the machine. This is the only resource that is required to install and operate the Co-signer on the host machine. The Co-signer's database is encrypted and stored on the host machine's disk, serving as the Co-signer's persistent storage. > [!WARNING] > > **Important**: Allocate a separate machine for each Co-signer to prevent conflicts and ensure isolation, enhancing security. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/1ac3082630bdf2681904ab3d00f491f79bd5f8ef4025df3d6c3958eb9342566f-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=e03c764718b4c83de4c47a4d3cbe1bdd) ## Secure Co-signer database encryption scheme The following process is implemented to build and secure the Co-signer's database: 1. The Co-signer, upon initialization, connects to the [Intel-SGX Remote Attestation server](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/attestation-services.html) that belongs to the Fireblocks SaaS and retrieves the key that encrypts its database: `FBKS-DB-Key` 2. The Co-signer initializes its encrypted database using the key `FBKS-DB-KEY` 3. The Co-signer encrypts `FBKS-DB-KEY` using the SGX hardware key of the host machine. 4. The Co-signer saves its encrypted database and the encrypted form of `FBKS-DB-KEY` in the host machine, serving as its persistent storage 5. The Co-signer cannot operate unless it is attested against Fireblocks' Remote Attestation server. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/a3cd183923542641d62c25dfe6000bc85c4c2db7a2151bdbb51ef19b31dda23b-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=a0bbb047d06aa91ed17bdcd7c70afafb) ## Installation artifacts The SGX Co-signer is comprised of several components: 1. Installation script: This is the CLI interface for installing and managing the Co-signer. You can retrieve the installation script from the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). The script includes the path to a Docker image registry for downloading the Co-signer Docker image. It supports downloading images of various versions, with default versions specified if none are explicitly provided. See the [SGX Co-signer version history](/cosigners/sgx/api-cosigner-versions-sgx) for details on the image version. 2. Docker image: The Co-Signer's executable is wrapped in a Docker image to provide safe, versioned, and consistent installation across different platforms. The Docker image provides all the libraries and dependencies of the main executable. 3. Main loading executable: A slim binary that downloads and verifies the enclave and exposes command line interfaces. 4. Secure SGX enclave: This component contains most of the Co-Signer's code and logic. It is both encrypted and signed by Fireblocks, and it can be downloaded from the Fireblocks server during setup. * **Q: Should I verify the executable's signature?**\ If you are using the correct Docker image, as specified by the script, there is no need for any other verification, as it is embedded in the image. * **Q: Why is the enclave encrypted?**\ SGX allows for saving the enclave encrypted on disk with a hardware key only available to the specific CPU where the enclave would be executed. Secured enclaves prevent the risk of modification, make it impossible to reverse engineer, and hide any potential secrets embedded within them. This enclave alone is capable of reading the secrets stored by the Co-Signer. * **Q: Should I verify the enclave's signature?**\ There is no need for that. The Fireblocks signing key is embedded within the loading executable, which verifies the enclave's signature whenever it is loaded. * **Q: Where do I find the enclave in the docker image?**\ The enclave is only downloaded when the Co-signer goes through a setup or upgrade process, so it is omitted from the Docker image. The name of the file is `enclave.signed.so`. The chain of trust follows the sequence: **script > image > executable > enclave**, with Fireblocks ensuring compatibility between these components. During setup or upgrade, the Co-signer downloads the latest enclave version from Fireblocks servers, which is guaranteed to be compatible with the installed executable. To ensure the enclave uses the latest published version, always use the most recent Co-signer Docker image. The safest approach is to install the Co-signer using the latest installation script. # SGX API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/alibaba/sgx-maintenance-alibaba > [!NOTE] > > You must have root privileges on the Co-signer machine to perform maintenance operations. Ensure you are logged in as a root user or use `sudo` to execute the commands. ## View the logs You can export the logs to a file in the local directory, tagged with the current date and time, by running the following command: ```bash theme={"system"} ./cosigner logs ``` Append a number to the command to retrieve the specified most recent amount of lines. By default, the Co-signer uses the Docker logging container. However, for better monitoring, the logs can be streamed to an external log delivery service, such as ELK, Splunk, or Datadog. The SGX Co-signer generates two different types of logs for troubleshooting and auditing: * `run.log` is an automatically generated log containing all of your Co-signer setup and configuration history. * `customer_cosigner.log` contains workspace operation data, such as the workspace's transaction approving and signing history and new connection approvals. These log files are separated into 10 files limited to 4.1MB each. They are automatically overwritten in chronological order. Your API Co-signer logs are structured using the following Regular Expression (RegEx): ```bash theme={"system"} ${AppName}:${Line} ${LogLevel} ${dd/MM/yyyy} ${HH:mm:ss,fff} ${ClassName} ::${MethodName} - ${LogMessage} ``` These logs are collected by the Docker container’s `STDOUT` stream and written into a log file on the container’s host machine at the location specified below. These log files are rotated with a max file size of 4MB for each log file. ```text theme={"system"} /databases/cosigner/log/customer_cosigner.log /databases/cosigner/log/customer_cosigner.log.1 /databases/cosigner/log/customer_cosigner.log.2 ... ``` > [!NOTE] > > The Co-signer is Docker-based and uses the Docker JSON file driver for logging. By default, the driver uses a standard logging output but can be configured to support other logging mechanisms. Refer to the official Docker documentation for more detailed information. *** ## Verify SGX is enabled on the machine After completing your server creation or installation, verify that SGX is enabled on the server. The Fireblocks API Co-Signers can only run on SGX-enabled servers with the latest patches, and verifying your configuration helps ensure you do not run into any potential issues later on. 1. Run the following commands on the server: ```bash theme={"system"} sudo apt update sudo apt upgrade sudo apt install cpuid cpuid -1 | grep -i sgx ``` 2. Verify the following: 1. SGX: Software Guard Extensions supported is `true`. 2. SGX\_LC: SGX launch config supported is `true`. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/0c0a9cb9db9029ad2559a58ddcde807472c7674dc0a81d25cd6dd58f57f37c5b-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=128b5d0f685e48863e18cc368d7d140b) > [!NOTE] > > Azure Standard\_DC\*\_v2 instances do not support SGX2, but it is not necessary to run the signer. *** ## List the paired API users You can list all API users paired with the Co-signer across the connected workspaces by running the following command: ```bash theme={"system"} ./cosigner list-users ``` The output will display a list of all API users paired with your Co-Signer, including the workspace name they are connected to, the API user's ID (its API key), and the associated Callback Handler server URL (if applicable). *** ## Retrieve the public key You can retrieve the Co-signer's public key, used by your optional Callback Handler server to authenticate requests from the Co-signer, by running the following command: ```bash theme={"system"} ./cosigner print-public-key ``` *** ## Stop the Co-signer You can stop the Co-signer by running the following command: ```bash theme={"system"} ./cosigner stop ``` *** ## Start the Co-signer You can Start the Co-signer by running the following command: ```bash theme={"system"} ./cosigner start ``` *** ## Restart the Co-Signer You can restart the Co-signer by running the following command: ```bash theme={"system"} ./cosigner restart ``` *** ## Retrieve the running image version You can retrieve your running Co-Signer version by running the following command: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image' + " " + '.[0].Image' ``` *** ## Retrieve the script version You can retrieve your current Co-Signer script version by running the following command: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` *** ## Update the Co-signer In the Fireblocks Console, go to [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer) and copy the download link of the Intel SGX installation script. The link is valid for seven days. Use the `curl` command to download the script to the path where the existing Co-Signer script is present. Paste the copied URL into the following command: ```bash theme={"system"} curl -o cosigner "URL Download Link" ``` > [!NOTE] > > If you have any issues with finding the installation script URL, please contact [Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=360003372200\&tf_360023089139=global_settings\&tf_360023089159=get_api_co-signer_installation_script). After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` Stop the Co-signer container by running the following command: ```bash theme={"system"} ./cosigner stop ``` Run `docker ps -a` and check the output. There should be no Co-signer container running. Backup the Co-signer's database by running the following command: ```bash theme={"system"} mv /databases/cosigner/enclave/enclave.signed.so /databases/cosigner/enclave/enclave.signed.so_old ``` Remove the current revision file: ```bash theme={"system"} sudo rm -f .revision ``` You can now update the Co-signer to the latest version by running the following command: ```bash theme={"system"} ./cosigner start ``` You can update to a specific image version by replacing the `` : ```bash theme={"system"} ./cosigner start ``` Run `docker ps -a` and check the output. You should see the Co-signer container running. If the update process did not succeed, try re-installing the Co-signer using the same version, but first remove the current version of the Co-Signer by running the following commands: ```bash theme={"system"} sudo rm -rf /databases sudo rm -f .local_config .revision ``` and then run: ```bash theme={"system"} ./cosigner setup ``` If that still doesn't work, refer to [API Co-signer Troubleshooting](/cosigners/operations/api-cosigner-troubleshooting). *** ## Migrate to a new machine Begin by setting up a new SGX-capable machine in your cloud or on-premises environment. Migrating the SGX Co-Signer to a new machine is most effectively performed using backup data from the existing machine. This approach minimizes downtime and reduces the risk of interruptions to your workspace operations. The following files and folders are generated automatically during the SGX Co-signer setup and are essential for backup and recovery purposes: 1. `/databases/cosigner/db`: The Co-signer DB folder stores the `secrets.db` file, the Co-Signer's secrets database. This file is encrypted with a key generated in a secure SGX enclave. 2. `/databases/cosigner/backup`: The backup folder maintained by the Co-Signer stores the backup upon any change to the database. The files are formatted using the following timestamp: `%Y%m%d%H%M%S.db` (e.g. `20220710184350.db`) based on the backup's date. 3. `/databases/cosigner/enclave/ra_loader_enclave.signed.so` - The enclave loader. Backup the files `secrets.db` and `ra_loader_enclave.signed.so` and copy them to the same folder locations on the new server: * `/databases/cosigner/db/secrets.db` * `/databases/cosigner/enclave/ra_loader_enclave.signed.so` After the above files have been copied to the new server, run `./cosigner start`. If you don't have backups of either `secrets.db` or `ra_loader_enclave.signed.so` files, unpair (re-enroll) the API users that are associated with the Co-signer and set up a new Co-signer on the new machine. Install the replacement on [Azure](/cosigners/azure/install-api-cosigner-azure), [Azure Marketplace](/cosigners/azure/install-api-cosigner-azure-marketplace), [IBM Cloud](/cosigners/ibm/install-api-cosigner-ibm), [Alibaba Cloud](/cosigners/alibaba/install-api-cosigner-alibaba), or [on-premises](/cosigners/on-premises/install-api-cosigner-onprem). # SGX API Co-signer Version History Source: https://developers.fireblocks.com/cosigners/alibaba/sgx-versions-alibaba Fireblocks updates the API Co-signer software to include stability enhancements and new features. This article provides details about the most recent SGX Co-signer versions. Always use the latest version listed here for optimal performance. ## Latest SGX Co-signer image version ### **Version 2026.10.21 (October 2026)** **Version hash:** `90324e06` * Callback enhancement: the number of change outputs is now included in the transaction metadata sent to the callback handler * Base image and root CA updates * Security fixes and updates * Support RHEL 9 and Ubuntu 26.04 hosts *** ## SGX Co-signer image version history ### **Version 2026.10.03 (October 2026)** **Version hash:** `77774ae4` * Security and bug fixes ### **Version 2026.09.16 (September 2026)** **Version hash:** `0449f81f` * Fast signing for EdDSA * Base image updated * Security updates ### **Version 2026.08.19 (August 2026)** **Version hash:** `f5ab4d7f80` * Callback responses may now be signed with ECDSA P-256 (ES256) in addition to RSA (RS256) * Security fixes ### **Version 2026.07.20 (July 2026)** **Version hash:** `86d1be9f9b` * Source and destination tags are now sent to the callback handler * Security improvements and bug fixes ### **Version 2026.06.24 (June 2026)** **Version hash:** `80e34d008a` * Performance improvements and bug fixes ### **Version 2026.05.27 (May 2026)** **Version hash:** `1b31e6b7fb` * Support for Fireblocks BCM * Support for batch attachment of tags * Performance improvements and bug fixes ### **Version 2026.04.27 (April 2026)** **Version hash:** `1d5b48eeef` * Performance improvements, security enhancements and bug fixes ### Version 2026.03.02 (March 2026) **Version hash:** 95a317d3c7 * Added support for POLICY\_CHANGE approval request type (the customer co-signer can now approve policy changes via Policy Service v2) * Performance improvements and bug fixes ### Version 2026.01.29 (January 2026) **Version hash:** 340d9f6057 * Bug fixes and performance improvements ### Version 2025.12.30 (December 2025) **Version hash:** a294449c1b * Added support for "Protected Tags" approval requests ### Version 2025.12.11 (December 2025) **Version hash:** c44604398e * The callback handler can now authenticate using a certificate signed by a root CA certificate preloaded to the Co-signer * The callback handler can now authenticate using both JWT and a certificate provided directly to the Co-signer (or signed by a root CA certificate provided to the Co-signer) * The callback can now return a "retry" response on exchange withdraw operation ### Version 2025.10.09 (October 2025) **Version hash:** 4e34888c01 * Fixed bug parsing messages after websocket reconnect. * Fixed bug sending messages on half-closed websocket connections. ### Version 2025.08.12 (August 2025; US environments only) **Version hash:** 19c07421dc * Fixed WebSocket bug causing performance issues ### Version 2025.07.16 (July 2025) **Version hash:** bb61e507 * Fixed bugs and increased performance * Better support for web sockets ### Version 3.10.1 (April 2025) **Version hash:** 7160d7b833 * Bug fixes and performance improvements ### Version 3.7.1 (October 2024) **Version hash:** 4ec196aec5 * Decreased signing latency, along with general performance increases * Remote management capabilities through Fireblocks Console * Support for more types of approval requests * Various fixes and improved handling of internal errors * Timing details are logged after every outgoing network request * The cosigner version will be reflected in the HTTP User-Agent header ### Version 3.6.5 (February 2024) **Version hash:** 3b807e2af7 * Improved logging for easier problem-solving * Improved stability, security, and performance * Enhanced deployment flexibility * Added support for the Callback Handler retry mechanism, enabling the Callback Handler to respond asynchronously * Updated major and minor dependencies * Preliminary support for upcoming features * Bug fixes ### Version 3.5.0 (August 2023) * [MPC-CMP security enhancements](https://support.fireblocks.io/hc/en-us/articles/10167020461596-MPC-CMP-security-enhancements-version-update-August-2023) * Bug fixes ### Version 3.3.0 (January 2022) * Added support for HTTP network proxy on outbound requests * Bug fixes *** ## Latest SGX Co-signer script version ### Version 2025.10.11 (December 2025) * New default Docker image: 2025.10.11 ## SGX Co-Signer script version history ### Version 2025.10.09 (October 2025) * New default Docker image: 2025.10.09 * Switched to Calendar Versioning to align script versions with default image versions ### Version 1.1.13 (August 2025) * New default Docker image 2025.08.12 ### Version 1.1.11 (April 2025) * New default Docker image 3.10.1 ### Version 1.1.10 (January 2025) * New default Docker image 3.7.1 ### Version 1.1.9 (February 2024) * New default Docker image 3.6.5 ### Version 1.1.8 (January 2024) * Added support for Callback Handler configuration via Azure Marketplace * Same default Docker image as in image version 3.5.0 and used as in script version 1.1.7 ### Version 1.1.7 (August 2023) * New default Docker image 3.5.0 ### Version 1.1.6 (February 2023) * Added support for Docker version 1.29.2 * Added validation for microcode: * Additional information about the microcode version appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem * Added validation to make sure Hyper-Threading is disabled * If Hyper-Threading is enabled, additional information appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem ### Version 1.1.5 (February 2022) * New default Docker image 3.3.0 * Added support for Co-signers in high-availability * Updated authentication model * Added support for HTTP network proxy on outbound requests *** ## Checking SGX Co-signer versions ### How do I check the version of my running SGX Co-signer image? To check your running SGX Co-signer image version, run the following command on your VM: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image +" " + . [0].Image' ``` ### How do I check the version of my running SGX Co-signer script? To check your running SGX Co-signer script version, run the following command on your VM: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` # AWS Nitro API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/aws/api-cosigner-maintenance-aws-nitro > [!NOTE] > > You must have root privileges on the Co-signer machine to perform maintenance operations. Ensure you are logged in as a root user or use `sudo` to execute the commands. ## View the logs You can export the logs to a file in the local directory, tagged with the current date and time, by running the following command: ```bash theme={"system"} ./fireblocks/cosigner logs ``` Append a number to the command to retrieve the specified most recent amount of lines. The Co-signer's logs are saved on the EC2 instance in the following file location: `/var/log/customer_cosigner.log` The log policy is as follows: ```xml theme={"system"} `` `` `` ``` *** ## Observe the status You can observe the Co-signer's status by running the following command from the EC2 instance: ```bash theme={"system"} ./fireblocks/cosigner get-status ``` It should return an output similar to the following: ```json theme={"system"} [root@ip-x-x-x-x ~]# ./fireblocks/cosigner get-status ========= Cosigner Status ========= Enclave Name: cosigner Enclave ID: i-0b11aeabc7d3bee3d-enc190641e6f0ce80a Process ID: 377331 Enclave State: RUNNING Service State: ACTIVE =================================== Latest Service Messages:------------------------ Jun 29 13:10:41 ip-x-x-x-x.us-east-2.compute.internal start_service.sh[377329]: Jun 29 13:10:41 ip-x-x-x-x.us-east-2.compute.internal start_service.sh[377329]: 1, 3 Jun 29 13:10:41 ip-x-x-x-x.us-east-2.compute.internal start_service.sh[377329]: ], Jun 29 13:10:41 ip-x-x-x-x.us-east-2.compute.internal start_service.sh[377329]: "MemoryMiB": 4096 Jun 29 13:10:41 ip-x-x-x-x.us-east-2.compute.internal start_service.sh[377329]: } =================================== ``` If you get anything other than `ACTIVE` in the "Service State" field, there’s a problem. Contact Fireblocks support and **attach the Co-signer’s logs so we can investigate**. *** ## List the paired API users You can list all API users paired with the Co-signer across the connected workspaces by running the following command: ```bash theme={"system"} ./fireblocks/cosigner list-users ``` The output will display a list of all API users paired with your Co-Signer, including the workspace name they are connected to, the API user's ID (its API key), and the associated Callback Handler server URL (if applicable). *** ## Retrieve the public key You can retrieve the Co-signer's public key, used by your optional Callback Handler server to authenticate requests from the Co-signer, by running the following command: ```bash theme={"system"} ./fireblocks/cosigner print-public-key ``` *** ## Stop the Co-signer You can stop the Co-signer by running the command: ```bash theme={"system"} ./fireblocks/cosigner stop ``` *** ## Start the Co-Signer You can Start the Co-signer by running the command: ```bash theme={"system"} ./fireblocks/cosigner start ``` *** ## Restart the Co-Signer You can restart the Co-signer by running the command: ```bash theme={"system"} ./fireblocks/cosigner restart ``` *** ## Update the Co-signer In the Fireblocks Console, go to [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer) and copy the download link of the AWS Nitro installation package. The link is valid for seven days. Use the `wget` command to download the package directly to the EC2 machine. Paste the copied URL into the following command: ```bash theme={"system"} wget -O nitro-cosigner.tar.gz "URL" ``` > [!NOTE] > > If you have any issues with finding the installation package URL, please contact [Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=360003372200\&tf_360023089139=global_settings\&tf_360023089159=get_api_co-signer_installation_script). Unpack the installation package by running the following command: ```bash theme={"system"} tar -xzf nitro-cosigner.tar.gz ``` Now run the following commands to stop the Co-signer service and update it: ```bash theme={"system"} systemctl stop cosigner ./install.sh --update ``` > **Running a Co-signer version earlier than 2026.05.27?** > > Run the following commands to stop the Co-signer service and update it by forcing a new installation: > > ```shellscript theme={"system"} > systemctl stop cosigner > ./install.sh --force > ``` > > The script will prompt for the following parameters: > > * Pairing token > * S3 bucket > * ARN of the CMK > > Use the same settings that were used to install the existing running version. These settings can be found in the file `/opt/fireblocks/env.txt`, where they are labeled as follows: > > * PAIRING\_TOKEN > * BUCKET\_NAME > * KEY\_ARN > > It will take about a minute to reinstall, and then the Co-signer will load using the new version. *** ## Migrate to a new machine > [!NOTE] > > Since the logs are saved to the EC2 instance, you might want to save them before terminating the machine. Throughout the migration process, refer to the [AWS Nitro Co-signer installation guide](/cosigners/aws/install-api-cosigner-aws), as some operations are identical. Follow these steps to migrate the Co-signer to a new EC2 machine: 1. Set up a new EC2 Nitro-capable instance. 2. Download the installation package to the new instance. 3. Create a new API user that will be used to connect to the new Co-signer instance. 4. Stop the running Co-signer operation by executing the command `systemctl stop cosigner` on the existing EC2. 5. Run the installation script and provide the same S3 bucket and CMK values when prompted to enter parameters during the installation. *** ## Configure a proxy server By default, the Co-signer is configured to communicate directly with Fireblocks SaaS without using a proxy server. Since the Co-signer uses certificate pinning for secure communication with Fireblocks SaaS, **only a transparent proxy can be used** between the Co-signer and Fireblocks SaaS. To configure a proxy server, add the key value `HTTPS_PROXY="URL"` as an environment variable to the following file: `/opt/fireblocks/env.txt` ```bash theme={"system"} HTTPS_PROXY="URL" # Replace URL with your transparent proxy server ``` Changing the proxy server settings requires restarting the Co-signer. Run this command to restart it: ```bash theme={"system"} systemctl restart cosigner ``` *** ## Configure the communication protocol By default, the Co-signer is configured to use WebSocket to communicate with Fireblocks SaaS. You can switch to HTTPS Long Polling by turning WebSocket off. To turn WebSocket off, add the key value `WEBSOCKET=0` as an environment variable to the following file: `/opt/fireblocks/env.txt` ```bash theme={"system"} WEBSOCKET=1 # Use WebSocket WEBSOCKET=0 # Use HTTPS Long Polling ``` Switching between the communication modes requires restarting the Co-signer. Run this command to restart it: ```bash theme={"system"} systemctl restart cosigner ``` On version 2026.04.27 or later, you can also select the protocol when you install the Co-signer, by setting `websocket` in the installation configuration file. On version 2026.10.21 or later, you can set `callback_retry_mechanism` instead. See [Install without prompts using a configuration file](/cosigners/aws/install-api-cosigner-aws#install-without-prompts-using-a-configuration-file). ### When to use HTTPS Long Polling Use HTTPS Long Polling when your Callback Handler returns `RETRY` responses. Fireblocks only redelivers a request after a `RETRY` response over HTTPS Long Polling. Over WebSocket the request is not retried, and the transaction stays in `PENDING_SIGNATURE` until it expires. Configuring a proxy also selects HTTPS Long Polling, because the Co-signer's WebSocket client cannot traverse an HTTPS proxy reliably. WebSocket is faster, so keep it unless one of these applies. # AWS Nitro API Co-signer Version History Source: https://developers.fireblocks.com/cosigners/aws/api-cosigner-versions-aws Fireblocks updates the API Co-signer software to include stability enhancements and new features. This article details the most recent AWS Nitro Co-signer versions. Always use the latest version listed here for optimal performance. ## Latest version ### **Version 2026.10.21 (October 2026)** **Version hash:** `90324e06` * Callback enhancement: the number of change outputs is now included in the transaction metadata sent to the callback handler * Upgrades now preserve the WebSocket setting * Base image and root CA updates * Security fixes and updates *** ## Version history ### **Version 2026.10.03 (October 2026)** **Version hash:** `77774ae4` * Supporting multiple NO\_PROXY domains * Security and bug fixes ### **Version 2026.09.16 (September 2026)** **Version hash:** `0449f81f` * Fast signing for EdDSA * Base image updated * Security updates ### **Version 2026.08.19 (August 2026)** **Version hash:** `f5ab4d7f80` * Hybrid callback authentication support * Callback responses may now be signed with ECDSA P-256 (ES256) in addition to RSA (RS256) * Security fixes ### **Version 2026.07.20 (July 2026)** **Version hash:** `86d1be9f9b` * Source and destination tags are now sent to the callback handler * Security improvements and bug fixes ### **Version 2026.06.24 (June 2026)** **Version hash:** `80e34d008a` * Performance improvements and bug fixes ### **Version 2026.05.27 (May 2026)** **Version hash:** `1b31e6b7fb` * Simpler, smarter upgrade - `./install.sh --update`. Automatically detects and migrates existing configuration. * Support for Fireblocks BCM * Support for batch attachment of tags * Performance improvements and bug fixes ### **Version 2026.04.27 (April 2026)** **Version hash:** `1d5b48eeef` * Performance improvements, security enhancements and bug fixes ### Version 2026.03.02 (March 2026) **Version hash:** 95a317d3c7 * Added support for POLICY\_CHANGE approval request type (the customer co-signer can now approve policy changes via Policy Service v2) * Performance improvements and bug fixes ### Version 2026.01.29 (January 2026) **Version hash:** 340d9f6057 * Bug fixes and performance improvements ### Version 2025.12.30 (December 2025) **Version hash:** a294449c1b * Added support for "Protected Tags" approval requests ### Version 2025.12.11 (December 2025) **Version hash:** c44604398e * The callback can now return a “retry” response in an exchange withdraw operation ### Version 2025.11.20 (November 2025) **Version hash:** 82a5cc0078 * Fixed a bug that may cause the Co-signer not to load correctly after a graceful shutdown ### Version 2025.10.09 (October 2025) **Version hash:** 4e34888c01 * Fixed bug parsing messages after websocket reconnect * Fixed bug sending messages on half closed websocket connections ### Version 2025.08.12 (August 2025; US environments only) **Version hash:** 19c07421dc * Fixed WebSocket bug causing performance issues ### Version 2025.07.16 (July 2025) **Version hash:** bb61e507 * Fixed bugs and increased performance * Better support for web sockets ### Version 2.1.1 (April 2025) **Version hash:** 7160d7b833 * Bug fixes and performance improvements ### Version 2.0.5 (September 2024) **Version hash:** d177e696c3 * Decreased signing latency along with general performance increases * Extended filtering of environment variables * Improvements to key handling within the enclave * Bug fixes ### Version 2.0.4 (August 2024) **Version hash:** 41d1888dc8 * Fixed the issue when setting AWS region using environment variables * Removed unnecessary testing artifacts ### Version 2.0.2 (August 2024) * Modified KMS CMK ARN prompt * Added start, stop and restart commands * Added echo to show where logs are saved to * Fixed AWS NTP sync issue * Fixed Callback Handler public key issue on install * Removed unnecessary testing artifacts ### Version 2.0.1 (July 2024) * Added check for force flag and existence of destination before the user is prompted for details * Added route DNS queries to the DNS server defined on the host EC2 instance ### Version 2.0.0 (June 2024) * Initial release # AWS Nitro API Co-signer Architecture Source: https://developers.fireblocks.com/cosigners/aws/aws-nitro-api-co-signer ## AWS resources used by the Co-signer The Fireblocks AWS Nitro API Co-signer leverages AWS Nitro Hypervisor technology and attestation mechanisms. It utilizes the following AWS resources: * **EC2 Instance**: Nitro-capable VM, through which the enclave operates. * **S3 Bucket**: used as the Co-signer's persistent storage and holds the encrypted database of the Co-signer. * **KMS Customer Managed Key**: used to securely protect the Co-signer's MPC keyshares, which are stored in the Co-signer's persistent storage within an S3 bucket. * **IAM Role**: used to tie everything together by granting only the necessary permissions to the specific resources. > [!WARNING] > > **Important**: Allocate a separate set of resources for each Co-signer to prevent conflicts and ensure isolation, enhancing security. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/0fcf441b913489197f949f49fef092cf503d20066a8efca77d272ce59c8094c4-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=52f94a0227ebeb036c5f7b3314b916b0) ## Co-signer attestation Fireblocks uses Nitro’s Platform Configuration Register (PCR) as an attestation mechanism to ensure the enclave image file that is accessing the resources at runtime is signed by Fireblocks. In AWS, **PCR8 (Platform Configuration Register 8)** is a part of the **Trusted Platform Module (TPM)** ecosystem and is specific to environments involving Nitro Enclaves or other secure computing contexts. In the AWS Nitro Co-signer, **PCR8** measures specific details about the Co-signer during the secure boot process. It ensures setup integrity by validating Fireblocks' enclave image signature when accessing the Customer Managed Key (CMK). ## Secure Co-signer database encryption scheme The following process is implemented to build and secure the Co-signer's database: 1. The user creates and configures a symmetric Customer Managed Key (CMK) in the KMS. 2. The Co-signer, upon initialization, asks KMS to generate an additional AES 128-bit CBC key: `FBKS-DB-Key` 3. The Co-signer initializes its encrypted database using the key `FBKS-DB-KEY` 4. The Co-signer encrypts `FBKS-DB-KEY` using the CMK. 5. The Co-signer saves its encrypted database and the encrypted form of `FBKS-DB-KEY` in the S3 bucket, serving as its persistent storage 6. The access to KMS, including the CMK, is restricted using a Fireblocks enclave image attestation signature and the IAM role and policies that were created by the user. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/e644e8af981f09f1bf02217503379cd63726472c068eb2208baa44eaa527ed98-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=02646dd392d611cf027b3ee39888f604) ## AWS Nitro installation package The installation package contains an Enclave Image File (EIF) and a set of installation scripts. The EIF is built based on the image containing the common API Co-signer code and the AWS Nitro enclave functionality. This image is scanned for known vulnerabilities, and only then does the build process continue based on the AWS build procedure. # Install AWS Nitro API Co-signer Source: https://developers.fireblocks.com/cosigners/aws/install-api-cosigner-aws ## Overview To install a Nitro Co-signer in AWS and connect it to your workspace, follow these steps: 1. **Setup and configure your AWS environment:** Prepare your AWS environment by creating and configuring the required resources. Ensure it meets the necessary specifications and security settings. 2. **Add a Co-signer to the workspace using an API user:** Using the Fireblocks Console or APIs, create an API user and use it to add a Co-signer to the workspace. 3. **Install and connect the Co-signer to the workspace:** Download the installation script to the Nitro-capable EC2 machine and run the script to install the Co-signer. Once installation is complete, the workspace owner approves the new MPC key shares for the API user through the Fireblocks mobile app. You can now view the Co-signer and its paired API user in your Fireblocks Console. Additionally, you can retrieve information about them using the Co-signer APIs. *** ## Step 1: Set up and configure your AWS environment Proper configuration of your AWS environment is straightforward but must be performed step by step in the specified order. Any misconfiguration could compromise the Co-signer's functionality or security. ### Required AWS account permissions You must have the necessary AWS account permissions to enable network access to the required domains and to create and configure the following resources: * IAM Role * Customer Managed Key (CMK) * S3 bucket * Nitro-capable EC2 machine ### Step 1.1: Allowlist domains The EC2 instance needs outbound access to the domains below on port 443. The **Needed for** column shows when each domain is used: * **Install, upgrade, and operation:** keep the domain open while the Co-signer runs. * **Install and upgrade:** you can close the domain after installation, but reopen it before each upgrade. * **Install:** used only during the first installation. #### Fireblocks domains Fireblocks domains depend on the Fireblocks SaaS environment your workspace is connected to. | Purpose | Needed for | Global | Europe | Swiss | | - | - | - | - | - | | Fireblocks gateway | Install, upgrade, and operation | `mobile-api.fireblocks.io` | `eu2-mobile-api.fireblocks.io` | `eu-mobile-api.fireblocks.io` | | Certificate service | Install, upgrade, and operation | `s3signurl.fireblocks.io` | Not used in version 2026.10.03 | Not used in version 2026.10.03 | | Installation package download | Install and upgrade | `fb-prod-us-customers.s3.amazonaws.com` | `fb-eu2-prod-customers.s3.amazonaws.com` | `fb-eu-prod-customers.s3.amazonaws.com` | The installation package download domain is needed on the EC2 instance only if you download the package there, as described in [Step 3.2](#step-3-2-download-and-unpack-the-installation-package). #### AWS and third-party domains Replace `{region}` with your EC2 instance's region and `{bucket}` with the S3 bucket you create in [Step 1.3](#step-1-3-set-up-and-configure-an-s3-bucket). | Domain | Owner | Needed for | Used to | | - | - | - | - | | `kms.{region}.amazonaws.com` | AWS | Install, upgrade, and operation | Decrypt the Co-signer's data each time the enclave starts | | `{bucket}.s3.{region}.amazonaws.com` | AWS | Install, upgrade, and operation | Store and load the Co-signer's encrypted data each time the enclave starts | | `al2023-repos-{region}-*.s3.dualstack.{region}.amazonaws.com` | AWS | Install | Install the Nitro Enclaves CLI and other Amazon Linux packages | | `pypi.org` | Python Software Foundation | Install | Install the `thrift` Python package | | `files.pythonhosted.org` | Python Software Foundation | Install | Download the `thrift` Python package | The Co-signer also uses the EC2 Instance Metadata Service (`169.254.169.254`). This address is local to the instance and isn't reached over the internet. See the metadata hop limit requirement in [Step 1.6](#step-1-6-set-up-and-configure-an-ec2-instance). > [!NOTE] > > * For a better understanding of the Co-Signer's communication with the above URLs, refer to the [AWS Nitro API Co-signer Architecture](/cosigners/aws/aws-nitro-api-co-signer) article. > * If you cannot support DNS IP whitelisting with an advanced firewall or within the cloud itself, you must allow egress traffic to any destination on port 443. ### Step 1.2: Set up and configure an IAM role Identity and Access Management (IAM) roles are used to delegate access to your AWS resources. Create an IAM security role that ties everything together by granting only the necessary permissions to the specific resources. 1. From the Identity and Access Management (IAM) page, select **Roles** and **Create role**. On the Select trusted entity page, select: 1. **Trusted entity type:** AWS service 2. **Service or use case:** EC2 2. Select **Next**. On the Add permissions page, don’t make any selections. 3. Select **Next**. Enter the role’s name and select **Create role** without changing the trust policy. 4. Select the newly created role and copy the IAM ARN for the next steps. The IAM ARN has the following structure: `arn:aws:iam::{ACCOUNT-ID}:role/{ROLE-NAME}` When you finish, the Permissions policies section will be empty (as shown below). After setting up the S3 bucket and the Customer Managed Key (CMK) in the Key Management Service (KMS), you will return to Permissions policies to add the required policies for those resources. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/7e5cfac157f5cda0cc174c05f219867122f55864da6ef9f3e675691495a5fda7-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=1dc8791491f925613d11f7db4dbcc68c) ### Step 1.3: Set up and configure an S3 bucket Create the S3 bucket that serves as the Co-signer's persistent storage. 1. From the Amazon S3 page, select **Create bucket**. Enter its name, leave everything else as default, and create the bucket. 2. Select the newly created bucket and on the bucket’s details page, select **Permissions**. 3. In the Bucket policy section, select **Edit** and copy-paste the S3 policy from the text box below. 4. Select **Save changes** to complete the operation. 5. Select the newly created bucket and copy the Bucket ARN for the next steps. The Bucket ARN has the following structure: `arn:aws:s3:::\{BUCKET-NAME}` Make sure you replace the`{BUCKET-NAME}`, `{ACCOUNT-ID}` and `{ROLE-NAME}` with the correct values. ```json theme={"system"} { "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Principal": "*", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:AbortMultipartUpload", "s3:GetObjectAcl", "s3:PutObjectAcl", "s3:RestoreObject" ], "Resource": [ "arn:aws:s3:::{BUCKET-NAME}", "arn:aws:s3:::{BUCKET-NAME}/*" ], "Condition": { "ArnNotEquals": { "aws:PrincipalArn": "arn:aws:iam::{ACCOUNT-ID}:role/{ROLE-NAME}" } } } ] } ``` ### Step 1.4: Set up and configure a KMS Customer-Managed Key (CMK) Create a Customer-Managed Key (CMK) in KMS to safeguard the Co-signer’s MPC key shares. #### Create the key 1. Open the Key Management Service (KMS) page and select **Create key**. 2. On the Configure key page, select: 1. **Key type:** Symmetric 2. **Key usage:** Encrypt and decrypt 3. Expand the **Advanced options** section and select: 1. **Key material origin:** KMS 2. **Regionality:** Single-Region key. **Note:** Select the Multi-Region key only if the EC2 instance is in a different region. 4. Select **Next**. #### Add labels 1. On the Add labels page, set a display name for the key in the **Alias** field (e.g., `fireblocks-nitro-cosigner`). 2. Optionally, fill in the **Description** and **Tags** fields. 3. Select **Next**. #### Configure permissions 1. On the Define key administrative permissions page, leave the **Key administrators** section blank and leave the **Key deletion** option checked. 2. Select **Next**. 3. On the Define key usage permissions page, leave the **Key users** section blank. 4. Select **Next**. #### Finalize the key 1. On the Review page, scroll down to the Key policy section. 2. Paste the KMS policy in the Key policy section. 3. Select **Finish**. #### Get the key ARN 1. Select the newly created key. 2. Copy the KMS ARN for the next steps. The KMS ARN has the following structure: `arn:aws:kms:{KEY-REGION}:{ACCOUNT-ID}:key/{KEY-ID}` Ensure you replace `{ACCOUNT-ID}` and `{KEY-ID}` with the correct values. #### Understanding the policy warning When you paste the KMS policy, you might see the following warning: > [!NOTE] > > **Unsupported Action For Condition Key** > > The following actions: `kms:GetKeyPolicy` are not supported by the condition key `kms:RecipientAttestation:PCR8`. The condition will not be evaluated for these actions. We recommend that you move these actions to a different statement without this condition key. **Why you can safely ignore this warning:** * The `kms:RecipientAttestation:PCR8` condition is only evaluated on KMS calls that include an attestation report from the enclave. * AWS ignores the condition on actions that never carry a report in the request (such as `GetKeyPolicy` and `Encrypt`) and emits this advisory. * **Security impact:** None. All sensitive operations (`Decrypt`, `DeriveSharedSecret`, `GenerateDataKey`, `GenerateRandom`) remain protected by the PCR8 attestation check. * **Functionality impact:** Unaffected. This behavior is by design, and the key can be used by the Fireblocks Nitro Co-signer immediately. The value for PCR8 varies based on the Fireblocks SaaS environment to which you are connected. Copy the correct value into the CMK policy: | Fireblocks SaaS | `{PCR8-VALUE}` | | - | - | | Global | `da1d9eca20ce98ab4fdbc51f8e5a2307fd4c61829b7d8bff40976cd6676862c8f3476ff4bdd0f65ecf4a48d6eb3099a8` | | Europe | `fffc94d68a150b49dc39b23954c793cd1a4f7972f528a1ee258dc130b6cd9454e29ae72ef66bd9697a55e774e17a2d49` | | Swiss | `69b1fbe9fb4ea49e084fe6f9f9623d871bd22ceb5efad47b1a8d3708e6674868cad8bf088ced3976c2194360c7d58219` | ```json theme={"system"} { "Version": "2012-10-17", "Id": "key-consolepolicy-4", "Statement": [ { "Sid": "Enable enclave data processing for specific role", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{ACCOUNT-ID}:role/{ROLE-NAME}" }, "Action": [ "kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey", "kms:GenerateDataKeyPair", "kms:GenerateDataKeyWithoutPlaintext", "kms:GenerateDataKeyPairWithoutPlaintext", "kms:GenerateRandom", "kms:GetKeyPolicy" ], "Resource": "*", "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:PCR8": "{PCR8-VALUE}" } } }, { "Sid": "Allow GetKeyPolicy to co-signer", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{ACCOUNT-ID}:role/{ROLE-NAME}" }, "Action": "kms:GetKeyPolicy", "Resource": "*" }, { "Sid": "Allow policy management to root user", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{ACCOUNT-ID}:root" }, "Action": [ "kms:DescribeKey", "kms:GetKeyPolicy", "kms:PutKeyPolicy", "kms:CreateAlias" ], "Resource": "*" } ] } ``` ### Step 1.5: Edit the IAM role's policy Return to the IAM service and select the IAM role you created. 1. In the Permissions policies section, select **Attach policies**, select the `AmazonSSMManagedInstanceCore` policy, and then select **Add permissions**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/94dba93837abd2061803908a20600e6936504f0d3050ea2dafab3bf01b9d6665-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=5762491e668dfac43599197e2619fce6) 2. In the Permissions policies section, select **Create inline policy** and choose **JSON** in the Policy editor section. Copy-paste the IAM policy below in there. Finally, create the policy and assign it a name. Make sure you replace the `{BUCKET-NAME}`, `{KEY-REGION}`, `{ACCOUNT-ID}` and `{KEY-ID}` with the correct values. ```json theme={"system"} { "Version": "2012-10-17", "Statement": [ { "Sid": "ListBuckets", "Effect": "Allow", "Action": "s3:ListAllMyBuckets", "Resource": "*" }, { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::{BUCKET-NAME}" }, { "Sid": "WritePermissionsOnBucket", "Effect": "Allow", "Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:GetObjectAcl", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::{BUCKET-NAME}/*" }, { "Sid": "AccessToTheKey", "Effect": "Allow", "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey", "kms:GenerateDataKeyPair", "kms:GenerateDataKeyWithoutPlaintext", "kms:GenerateDataKeyPairWithoutPlaintext", "kms:GenerateRandom", "kms:GetKeyPolicy" ], "Resource": [ "arn:aws:kms:{KEY-REGION}:{ACCOUNT-ID}:key/{KEY-ID}" ] } ] } ``` ### Step 1.6: Set up and configure an EC2 instance > [!WARNING] > > Use only the c5.xlarge or c5a.xlarge instance types! > > AWS Nitro secure enclaves must have a minimum of two dedicated vCPUs and 4GB memory per enclave, and Nitro secure enclaves can utilize up to 50% of the host EC2 instance's resources. Therefore, the minimum requirement is to have an instance with four vCPUs and 8GB memory (`c5.xlarge`). 1. Create a Nitro-capable EC2 instance by selecting **Launch instances** from the EC2 Instances page. Enter its name and choose the Amazon Machine Image (AMI) **Amazon Linux 2023 AMI with 64-bit (x86) architecture**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/a5335b60d66ac36dba62fcc78e97e167d4578a666dbaca5166f7c2849dd26003-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=42c2771bdec78603cd6d4f455385e3df) 2. In the Instance type section, select `c5.xlarge` (4vCPUs, 8GB RAM) or `c5a.xlarge` if the former is not available in your region. 3. In the Network settings section, set where to allow SSH traffic from. We recommend restricting access as much as possible. 4. In the Advanced details section: 1. **IAM instance profile:** Select the IAM Role you created in the previous steps. 2. **Nitro Enclave:** Select **Enable**. This field will be grayed out if you selected an instance type that is incompatible with Nitro. 3. **Metadata version:** Select **V2 only (token required)**. 4. **Metadata response hop limit:** Set to a minimum of 2 (two). When you’re done, launch the instance. ### Step 1.7: Additional security recommendations * It is highly recommended to control user and network access to your AWS environment and EC2 instance by properly configuring your VPC and security groups. Learn more about [controlling network traffic](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/infrastructure-security.html#control-network-traffic) in the AWS documentation. * See [API Co-signer security checklist and recommended defense and monitoring systems](/cosigners/overview/co-signer-security-checklist-defense-monitoring) for further information. * See [AWS Nitro recommendations](#aws-nitro-recommendations) for EBS, CloudTrail, S3, and VPC settings. *** ## Step 2: Add a Co-signer to the workspace using an API user Use this guide to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Be sure to copy-paste (or otherwise record) the following items to your notes since they are required during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script *** ## Step 3: Install and connect the Co-signer to the workspace > [!NOTE] > > You must have root privileges on the EC2 instance to install the Co-signer > > Ensure you are logged in as a root user or use `sudo` to execute the installation commands. ### Step 3.1: Connect to your EC2 Instance Connect to your EC2 instance with your preferred method. ### Step 3.2: Download and unpack the installation package Using the download link of the AWS Nitro Co-signer installation package you copied from the Console, run the `wget` command to download the package directly to your machine. You can find the download link in the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). Paste the appropriate URL into the following command: ```bash theme={"system"} wget -O nitro-cosigner.tar.gz "URL" ``` Unpack the installation package by running the following command: ```bash theme={"system"} tar -xzvf nitro-cosigner.tar.gz ``` ### Step 3.3: Run the installation script The installation package contains an Enclave Image File (EIF) and installation scripts. To install the Co-signer, navigate to the directory where the installation package was unpacked and run: ```bash theme={"system"} ./install.sh ``` You will be prompted to enter the following parameters: * **Pairing token**: Enter the API user’s pairing token that you copied from the Fireblocks Console. This token pairs the API user with the Co-signer, establishing the connection between the Co-signer and the workspace. * **S3 bucket name**: Enter the name of the S3 bucket that you created before. You should only add the bucket name and not the full ARN of the bucket. For example: If your bucket ARN is `arn:aws:s3:::{BUCKET-NAME}`, you need to enter only `{BUCKET-NAME}`. * **CMK ARN:** Enter the Amazon Resource Name (ARN) of the CMK that you created before. At this stage, you will have the option to configure the Callback Handler parameters for the API user connecting the Co-signer to the workspace. This feature is optional. You can [configure it later through the Console, APIs, or locally](/cosigners/operations/api-cosigner-operate) from the Co-signer's host machine. For detailed instructions on setting up your Callback Handler's interface to the Co-signer and implementing its logic and code, refer to the [Setup API Co-signer Callback Handler](/reference/api-cosigner-setup-callback-handler) section. Once the installation is complete, the Co-signer will automatically start running. > [!NOTE] > > The Nitro cosigner installation script installs the below components as services > > * cosigner > * cosigner-networking > * cosigner-logging > * cosigner-env-export > > The cosigner service is started up during the execution of the installation script, and is also enabled to start on boot, which in turn starts up the three remaining services. #### Install without prompts using a configuration file > [!NOTE] > > This feature is supported by AWS Nitro Co-signer version 2026.04.27 or later. If you automate your deployments, you can supply the installation parameters in a YAML file instead of entering them at the prompts. Pass the file with `--config`, which runs the installation without any prompts: ```bash theme={"system"} ./install.sh --config config.yaml ``` The file uses flat key-value pairs. Nested objects and lists are not supported. Keys are not case-sensitive, and inline comments are ignored: ```yaml theme={"system"} key_arn: arn:aws:kms:us-east-1:123456789012:key/abc123 bucket_name: my-cosigner-bucket pairing_token: eyJhbGciOiJSUzI1... callback_url: https://my-callback.example.com callback_public_key: "-----BEGIN PUBLIC KEY-----\nABC...\n-----END PUBLIC KEY-----\n" https_proxy: http://proxy.example.com:3128 no_proxy: example.com,internal.example.org callback_retry_mechanism: yes ``` | Key | Required | Description | | - | - | - | | `key_arn` | Yes | The ARN of the CMK you created. | | `bucket_name` | Yes | The S3 bucket name, without the ARN prefix. | | `pairing_token` | Yes | The API user's pairing token from the Fireblocks Console. | | `callback_url` | No | The Callback Handler URL for this API user. | | `callback_public_key` | No | The Callback Handler public key, in PEM format. | | `https_proxy` | No | The proxy the Co-signer uses for outbound traffic. Setting a proxy also selects HTTPS Long Polling. Supported by version 2026.10.03 or later. | | `no_proxy` | No | Comma-separated domains that bypass the proxy. Supported by version 2026.10.03 or later. | | `websocket` | No | The communication protocol: `0` for HTTPS Long Polling, `1` for WebSocket. Defaults to `1`. See [Configure the communication protocol](/cosigners/aws/api-cosigner-maintenance-aws-nitro#configure-the-communication-protocol). | | `callback_retry_mechanism` | No | Whether your Callback Handler returns `RETRY` responses: `yes` selects HTTPS Long Polling, `no` selects WebSocket. Use either this key or `websocket`. If the two conflict, the installation stops with an error. Supported by version 2026.10.21 or later. | > [!NOTE] > > If your Callback Handler returns `RETRY` responses, set `callback_retry_mechanism: yes` or `websocket: 0`. Fireblocks only redelivers a request after a `RETRY` response over HTTPS Long Polling. To upgrade a Co-signer without prompts, run `./install.sh --update`. Update mode reuses the parameters from your existing installation, including the communication protocol, so you do not need to pass the configuration file again. ### Step 3.4: Approve MPC key shares for the API user If the API user paired with the Co-signer has an Admin or Signer role, the workspace Owner will receive a notification. This notification will prompt them to approve a new MPC key share request for that API user using the Fireblocks mobile app. The Owner must approve this request within five days (120 hours) of receiving it. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. Observe that it is online and that the API user is paired to it. > [!TIP] > > **Looking for the Co-signer's status and logs?** > > Refer to the [AWS Nitro Co-signer Maintenance](/cosigners/aws/api-cosigner-maintenance-aws-nitro) guide for more information. *** ## AWS Nitro recommendations * **EC2 - EBS Volume Encryption**\ Enabling encryption for all EBS volumes is strongly recommended to ensure data security and compliance with best practices. While Fireblocks uses AWS Nitro to safeguard sensitive workloads, customers are advised to configure EBS encryption based on their organization’s compliance requirements and data protection policies. Encryption can be managed seamlessly through AWS Key Management Service (KMS). * **CloudTrail - Enabling Service for all Regions**\ Fireblocks utilizes an organizational structure incorporating AWS Control Tower, a dedicated log-archive account, and automated processes to ensure comprehensive multi-region coverage for CloudTrail. While this approach is recommended, customers retain the flexibility to operate in single or multi-region modes based on their operational and regulatory requirements. Enabling CloudTrail for all applicable regions is crucial, however, to maintain visibility into account activities and meet audit requirements. * **S3 - Enabling MFA Delete**\ MFA Delete for S3 buckets is a security best practice for protecting against accidental or unauthorized data deletion. However, Fireblocks does not enforce specific bucket policies, which must align with each customer’s internal security frameworks. Customers are encouraged to evaluate and implement MFA Delete which supports their risk management strategy. * **S3 - Enabling Versioning**\ S3 versioning provides added protection for data backup and replication. However, Fireblocks' Co-Signer architecture does not require backup or replication of data stored in S3. Customers should assess the need for versioning based on their internal data resilience and disaster recovery policies. * **S3 - Enabling Access Logging**\ Enabling S3 access logging requires a dedicated logging bucket. Fireblocks recognizes that customers often have centralized logging infrastructures, forwarding logs to SIEM platforms for analysis and archiving. In such cases, enabling S3 access logging may result in redundancy. Customers must ensure their existing logging solutions sufficiently cover S3 access patterns for security monitoring and compliance. * **VPC - Enabling Subnet Flow Logs**\ While VPC Flow Logs are a valuable tool for monitoring network traffic, they can incur significant costs. Customers should evaluate the necessity of enabling VPC subnet flow logs based on their security posture, budgetary considerations, and compliance obligations. Fireblocks recommends allowing this feature to where granular network activity monitoring is required. # Install SGX Azure API Co-signer Source: https://developers.fireblocks.com/cosigners/azure/install-api-cosigner-azure ## Overview To install an SGX Co-signer in Microsoft Azure and connect it to your workspace, follow these steps: 1. **Setup and configure your Azure environment** Prepare your Azure environment by creating and configuring the required resources. Ensure it meets the necessary specifications and security settings. 2. **Add a Co-signer to the workspace using an API user** Using the Fireblocks Console or APIs, create an API user and use it to add a Co-signer to the workspace. 3. **Install and connect the Co-signer to the workspace** Download the installation script to the SGX-capable virtual machine and run the script to install the Co-signer. Once installation is complete, the workspace owner approves the new MPC key shares for the API user through the Fireblocks mobile app. You can now view the Co-signer and its paired API user in your Fireblocks Console. Additionally, you can retrieve information about them using the Co-signer APIs. *** ## Step 1: Setup and configure your Azure environment ### Step 1.1: Allowlist domains The Co-signer machine needs outbound access to the domains below on port 443, unless another port is listed. The **Needed for** column shows when each domain is used: * **Install, upgrade, and operation:** keep the domain open while the Co-signer runs. * **Install and upgrade:** you can close the domain after installation, but reopen it before each upgrade. * **Install:** used only during the first installation. #### Fireblocks domains Fireblocks domains depend on the Fireblocks SaaS environment your workspace is connected to. | Purpose | Needed for | Global | Europe | Swiss | | - | - | - | - | - | | Fireblocks gateway | Install, upgrade, and operation | `mobile-api.fireblocks.io` | `eu2-mobile-api.fireblocks.io` | `eu-mobile-api.fireblocks.io` | | Certificate service | Install, upgrade, and operation | `s3signurl.fireblocks.io` | `eu2-signurl.fireblocks.io` | `eu-s3signurl.fireblocks.io` | | Installation script download | Install and upgrade | `fb-prod-us-customers.s3.amazonaws.com` | `fb-eu2-prod-customers.s3.amazonaws.com` | `fb-eu-prod-customers.s3.amazonaws.com` | | Co-signer enclave download | Install and upgrade | `fb-cosigner-images.s3.amazonaws.com` | `fireblocks-eu2-prod-fr-cosigner.s3.amazonaws.com` | `fireblocks-eu-prod-fr-cosigner.s3.amazonaws.com` | The installation script download domain is needed on the Co-signer machine only if you download the script there, as described in [Step 3.1](#step-3-1-download-the-installation-script). #### Third-party domains | Domain | Owner | Needed for | Used to | | - | - | - | - | | `registry.gitlab.com` | GitLab | Install and upgrade | Pull the Co-signer container image | | `gitlab.com` | GitLab | Install and upgrade | Sign in to the container registry | | `cdn.registry.gitlab-static.net` | GitLab | Install and upgrade | Download the container image layers | | `download.docker.com` | Docker | Install | Install Docker Engine | | `github.com` | GitHub | Install | Download Docker Compose | | `release-assets.githubusercontent.com` | GitHub | Install | Download Docker Compose (GitHub redirects the download to this domain) | | `azure.archive.ubuntu.com` (port 80) | Canonical | Install | Install Ubuntu packages | The Co-signer also reads SGX attestation data from the Azure Instance Metadata Service (`169.254.169.254`). This address is local to the virtual machine and isn't reached over the internet, but don't block it on the machine itself. > [!NOTE] > > If you cannot support DNS IP whitelisting with an advanced firewall or within the cloud itself, you must allow egress traffic to any destination on port 443. ### Step 1.2: Create an Intel SGX virtual machine Follow this [Microsoft installation guide](https://learn.microsoft.com/en-us/azure/confidential-computing/quick-create-portal) and create an Intel SGX virtual machine through the Azure portal. > [!NOTE] > > Only the *Configure an Intel SGX virtual machine* section is required. The necessary settings are listed below. You do not need to complete the Connect to the Linux VM or Next Steps sections **The minimum requirements for the VM are:** * RAM: 16GB * Storage: 256GB * OS: * Ubuntu 22.04 LTS, 24.04 LTS, or 26.04 LTS (Canonical). Ubuntu 26.04 LTS requires API Co-signer version 2026.10.21 or later. * Latest Linux kernel version * Latest Intel microcode (BIOS update) * Install packages apt-update over port 80: [http://azure.archive.ubuntu.com/ubuntu](http://azure.archive.ubuntu.com/ubuntu) See the official [Microsoft documentation to find which products are available per region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=virtual-machines\®ions=all). Complete the following steps to create an SGX-capable VM in Azure: 1. Select Ubuntu 22.04 LTS, Ubuntu 24.04 LTS, or Ubuntu 26.04 LTS (Canonical) as the image, with the latest Intel microcode (BIOS update). The microcode is automatically updated on Azure. 2. Select your region. 3. Under the **Advanced** tab, select **Gen 2**. 4. Select Size. The recommended size is `Standard_DC4s_v3`. 5. Name your Co-signer VM and create it. The final resource setup window should look like this: ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/873a996626d00f25faed210e51f5ba80c36a4fa23672ae53c50f8b023cbd3d05-Screenshot_2024-12-06_122933.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=e5331c19721d8d896a63bec10149caa7) ### Step 1.3: Verify SGX is enabled on your VM After creating your virtual machine, confirm that SGX is enabled. The SGX Co-signer requires a server with SGX enabled and the latest patches applied. This verification ensures smooth operation and avoids potential issues. To verify that SGX is enabled on the VM, run the following commands with root privileges:: ```bash theme={"system"} apt update apt upgrade apt install cpuid cpuid -1 | grep -i sgx ``` Verify the following: 1. `SGX`: Software Guard Extensions supported is **true** 2. `SGX_LC`: SGX launch config supported is **true** ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/fe23ee1939feee21b46aa47061c3d01ab28840989133235e3d3b578d3722630a-Screenshot_2024-12-06_133814.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=79db17bab8bfbeff948a5374cf9a8363) ### Step 1.4: **Verify the required software packages** The installation script automatically installs Docker, Docker Compose, and PIP if not already present on the machine. No manual installation is required for these dependencies. > **Need to install the dependencies?** > > Here are the commands as they are executed in the install script: > > **Docker** > > **Ubuntu 22.04 and 24.04** > > ```text theme={"system"} > sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common > curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - > sudo add-apt-repository -y "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > sudo apt-get update > sudo apt-get install -y docker-ce docker-ce-cli containerd.io > ``` > > **Ubuntu 26.04** > > Ubuntu 26.04 no longer includes `apt-key`, so the installation script adds the Docker repository with a signed keyring instead: > > ```text theme={"system"} > sudo apt-get install -y ca-certificates curl > sudo install -m 0755 -d /etc/apt/keyrings > sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc > sudo chmod a+r /etc/apt/keyrings/docker.asc > echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo ${VERSION_CODENAME}) stable" | sudo tee /etc/apt/sources.list.d/docker.list > sudo apt-get update > sudo apt-get install -y docker-ce docker-ce-cli containerd.io > ``` > > **Docker Compose** > > ```text theme={"system"} > sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose > sudo chmod +x /usr/local/bin/docker-compose > ``` > > **PIP** > > ```text theme={"system"} > curl https://bootstrap.pypa.io/get-pip.py | python3 > ``` **Verify or install the SGX driver** On Ubuntu 22.04, 24.04, and 26.04, the SGX driver is built into the kernel. Before running the installation script, verify it is active: ```text theme={"system"} ls /dev/sgx_enclave /dev/sgx_provision ``` If both devices are listed, no further action is needed. Proceed to the next step. If the devices are not present, verify you are running on a the right machine type. ### Step 1.5: Additional security recommendations It is highly recommended to control user and network access to Co-signer's machine. See [API Co-signer security checklist and recommended defense and monitoring systems](/cosigners/overview/co-signer-security-checklist-defense-monitoring) for further information. *** ## Step 2: Add a Co-signer to the workspace using an API user Follow the instructions to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Ensure you copy to your clipboard the following items, which you will use during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script *** ## Step 3: Install and connect the Co-signer to the workspace > [!NOTE] > > You must have root privileges on the Co-signer machine to install the Co-signer. Ensure you are logged in as a root user or use `sudo` to execute the commands. ### Step 3.1: Download the installation script Using the download link of the SGX Co-signer installation script you copied from the Console, run the `curl` command to download the package directly to your machine. You can find the download link in the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). Paste the appropriate URL into the following command: ```bash theme={"system"} curl -o cosigner "URL" ``` ### Step 3.2: Run the installation script After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` To install the Co-signer, run: ```bash theme={"system"} ./cosigner setup ``` You will be prompted to enter the **Pairing token** for the API user, which you retrieve from the Fireblocks Console. This token pairs the API user with the Co-signer. At this stage, you will have the option to configure the Callback Handler parameters for the API user connecting the Co-signer to the workspace. This feature is optional. You can [configure it later through the Console, APIs, or locally](/cosigners/operations/api-cosigner-operate) from the Co-signer's host machine. For detailed instructions on setting up your Callback Handler's interface to the Co-signer and implement its logic and code, refer to the [Setup API Co-signer Callback Handler](/reference/api-cosigner-setup-callback-handler) section. The setup process validates the machine's hardware and installs the necessary drivers to support the appropriate SGX version and the SGX Co-signer's executable image. It includes an attestation flow to ensure that the SGX Co-signer's executable runs securely inside an Intel SGX enclave. Additionally, the script installs other required components. Once installation is complete, to start the cosigner run: `./cosigner start`. At the end of the process, the Co-Signer generates a JSON configuration file, which can be used for future configuration updates. ### Step 3.3: Approve MPC key shares for the API user If the API user used to pair with the Co-Signer and connect it to your workspace has an Admin or Signer role, the workspace owner will receive a notification. This notification will prompt them to approve a new MPC key share request for that API user using the Fireblocks mobile app. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. Observe it is online and that the API user is paired to it. **To check the Co-signer's status and observe the logs, see the SGX Co-signer Maintenance article.** # Install SGX Azure Marketplace API Co-signer Source: https://developers.fireblocks.com/cosigners/azure/install-api-cosigner-azure-marketplace The Fireblocks API Co-signer script is available as a managed and versioned component through [the Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/fireblocksinc1626390946623.co-signer?tab=Overview). This solution automates the deployment process, eliminating the need to create an SGX-enabled VM and install the Co-signer. To install an SGX Co-signer in Microsoft Azure and connect it to your workspace through the Azure Marketplace, complete the following steps. ## Step 1: Add a Co-signer to the workspace using an API user Follow the instructions to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Ensure you copy to your clipboard the following items, which you will use during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script ## Step 2: Set up your Azure environment, install and connect the Co-signer to the workspace ### 2.1. Azure prerequisites You must have a valid Azure Subscription with permissions to create Confidential Compute VMs, VNets, Resource Groups, and OS Disk at a minimum. Your subscription must also be registered for `Microsoft.Compute`, `Microsoft.Solutions` and `Microsoft.Network` service providers. Your Azure subscription must have Quota limits enabled for `Standard_DC4s_v3` VM. If you are unsure, check with your Azure Administrator. You may have to submit a support ticket with Microsoft to increase the quota limits. Also, your Azure subscription must have the following permissions: * `Microsoft.Solutions/locations/operationStatuses/read` * `Microsoft.Resources/deployments/write` * `Microsoft.Network/virtualNetworks/write` * `Microsoft.Network/networkInterfaces/write` * `Microsoft.Compute/virtualMachines/write` * `Microsoft.Compute/virtualMachines/extensions/write` ### 2.2. Using the marketplace automation Complete the following steps to deploy a new SGX Co-signer using the Azure Marketplace. #### Basics tab * Under **Subscription**, select or enter your existing Azure subscription where you want to deploy this Co-signer. * Under **Resource group**, select or create a group that properly organizes your resources within your subscription (i.e. geographic, commercial, sales affiliation, etc). * Under **Region**, select the geographic region where you want your virtual machine to be deployed. * Under **Virtual Machine**, select a name for your machine that is aligned with your best practices. Here you can also change the machine’s size, depending on your estimate of your projected transaction volume. We recommend DC4S as a minimum, but you may decide to change it based on your expected processing volume. * Under **Username**, enter a username, and we will create one for you with an admin role, which is necessary for the creation of your API Co-signer and for logging into and fully managing your Azure’s virtual machine. * Alternatively, if you are not interested in a username and password, you can upload a **Public key**. Once we receive it from you, we will use it to create a virtual machine for you, and you can use this key to log into the machine. * If you decide to go with a username, enter and confirm a **Password** of your choice. The password must meet the specifications listed on the marketplace. * Under **Managed application**, which is where we package all of your resources (the VM, Virtual network, storage), enter the name of your managed application (just as you entered the name of your Subscription above). * Similarly, under **Managed resource group**, enter the name of your Managed resource group (just as you entered the name of your Resource group above). * Select Next to move on to the next tab. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/a5d867a56bbc69b58337324502cb311c569f50432502ac7a9e3fee393de16cdc-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=17fe9b6c963cb1431e3879473e1fe2e4) #### API Cosigner Settings tab * Enter the API user's pairing token you copied from the Console * Enter the download link of the SGX Co-signer's installation script you copied from the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer) At this stage, you will have the option to configure the Callback Handler parameters for the API user connecting the Co-signer to the workspace. This feature is optional. You can [configure it later through the Console, APIs, or locally](/cosigners/operations/api-cosigner-operate) from the Co-signer's host machine. For detailed instructions on setting up your Callback Handler's interface to the Co-signer and implementing its logic and code, refer to the [Setup API Co-signer Callback Handler](/reference/api-cosigner-setup-callback-handler) section. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/8b82dfb44bdda9a22958f1d074f37260df48e4559591f1562ef8a37e0186a90b-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=db4d034b6fd933aefa8fc693e9c493d9) #### Review + Create tab In this tab, you can review all the information you provided in the previous sections and confirm it is accurate, or go back to modify whatever needs correction. Select **Create**, and the Azure Marketplace solution will initiate the creation of the Azure SGX API Co-signer after a few minutes. If the deployment fails, you will see an error on the Azure Marketplace portal along with details on its root cause. Refer to the Troubleshooting section below for potential issues that could cause such a failure. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/a30c536dbc80b41c9f1785731d6a7eeda0b2e8b60d9ca102f38838c6dfbec7fa-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=26973991f50b3562c7d90a142443994a) #### Co-signer script The Azure Co-signer script is installed in the home folder of the admin user (e.g., `/home/yourname/`). You can [operate](/cosigners/operations/api-cosigner-operate) and [maintain](/cosigners/sgx/api-cosigner-maintenance-sgx) the Co-signer using the Console or the script from the Azure VM, just as you would from the command line of an SGX machine you set up manually. ### 2.3: Approve MPC key shares for the API user If the API user used to pair with the Co-Signer and connect it to your workspace has an Admin or Signer role, the workspace owner will receive a notification. This notification will prompt them to approve a new MPC key share request for that API user using the Fireblocks mobile app. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. Observe it is online and that the API user is paired to it. ## Troubleshooting > [!NOTE] > > **Co-signer Maintenance** > > To check the Co-signer's status and observe the logs, see the [SGX Co-signer Maintenance](/cosigners/sgx/api-cosigner-maintenance-sgx) article. Since the solution is deployed on an Azure instance in your environment, a wide range of issues may occur depending on your configuration. Here are some common issues: * Quota limits on your subscription may prevent the provisioning of `Standard_DC4s_v3 VM`. You may have to request an increase in quota limits or open a support ticket with Microsoft to increase the quota limits. * Review the Azure deployment logs for any permissions-related errors. [Learn more about resolving these errors in the official Microsoft Azure documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-register-resource-provider?tabs=azure-portal). * Ensure your subscription has Microsoft.Compute, Microsoft.Solutions, and Microsoft.Network registered. * Your subscription should also have permission to create resource groups. * Your Azure subscription should have the permissions listed in the prerequisites section above. * Check if your pairing token has expired. If it has, renew the token and try again. * The API Co-signer script URL must be entered with opening and closing double quotes. * If you modified the default VM, make sure you selected an SGX-enabled VM. * Log a ticket with Fireblocks Support and attach the `*_run.log` files from the `/var/lib/waagent/custom-script/download/0` folder. * You may need to delete the VM and any resources created during the deployment of the solution. # Intel SGX Co-signer Architecture Source: https://developers.fireblocks.com/cosigners/azure/sgx-architecture-azure > [!NOTE] > > Learn how to install SGX Co-signer in Azure in the [following guide](/cosigners/azure/install-api-cosigner-azure). ## Resources used by the SGX Co-signer The Fireblocks SGX Co-signer utilizes Intel's SGX enclave and attestation mechanisms. It can be deployed either in cloud service providers that support compatible SGX servers or on-premise using a bare-metal server. Also, an SGX driver must be loaded into the machine. This is the only resource that is required to install and operate the Co-signer on the host machine. The Co-signer's database is encrypted and stored on the host machine's disk, serving as the Co-signer's persistent storage. > [!WARNING] > > **Important**: Allocate a separate machine for each Co-signer to prevent conflicts and ensure isolation, enhancing security. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/1ac3082630bdf2681904ab3d00f491f79bd5f8ef4025df3d6c3958eb9342566f-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=e03c764718b4c83de4c47a4d3cbe1bdd) ## Secure Co-signer database encryption scheme The following process is implemented to build and secure the Co-signer's database: 1. The Co-signer, upon initialization, connects to the [Intel-SGX Remote Attestation server](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/attestation-services.html) that belongs to the Fireblocks SaaS and retrieves the key that encrypts its database: `FBKS-DB-Key` 2. The Co-signer initializes its encrypted database using the key `FBKS-DB-KEY` 3. The Co-signer encrypts `FBKS-DB-KEY` using the SGX hardware key of the host machine. 4. The Co-signer saves its encrypted database and the encrypted form of `FBKS-DB-KEY` in the host machine, serving as its persistent storage 5. The Co-signer cannot operate unless it is attested against Fireblocks' Remote Attestation server. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/a3cd183923542641d62c25dfe6000bc85c4c2db7a2151bdbb51ef19b31dda23b-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=a0bbb047d06aa91ed17bdcd7c70afafb) ## Installation artifacts The SGX Co-signer is comprised of several components: 1. Installation script: This is the CLI interface for installing and managing the Co-signer. You can retrieve the installation script from the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). The script includes the path to a Docker image registry for downloading the Co-signer Docker image. It supports downloading images of various versions, with default versions specified if none are explicitly provided. See the [SGX Co-signer version history](/cosigners/sgx/api-cosigner-versions-sgx) for details on the image version. 2. Docker image: The Co-Signer's executable is wrapped in a Docker image to provide safe, versioned, and consistent installation across different platforms. The Docker image provides all the libraries and dependencies of the main executable. 3. Main loading executable: A slim binary that downloads and verifies the enclave and exposes command line interfaces. 4. Secure SGX enclave: This component contains most of the Co-Signer's code and logic. It is both encrypted and signed by Fireblocks, and it can be downloaded from the Fireblocks server during setup. * **Q: Should I verify the executable's signature?**\ If you are using the correct Docker image, as specified by the script, there is no need for any other verification, as it is embedded in the image. * **Q: Why is the enclave encrypted?**\ SGX allows for saving the enclave encrypted on disk with a hardware key only available to the specific CPU where the enclave would be executed. Secured enclaves prevent the risk of modification, make it impossible to reverse engineer, and hide any potential secrets embedded within them. This enclave alone is capable of reading the secrets stored by the Co-Signer. * **Q: Should I verify the enclave's signature?**\ There is no need for that. The Fireblocks signing key is embedded within the loading executable, which verifies the enclave's signature whenever it is loaded. * **Q: Where do I find the enclave in the docker image?**\ The enclave is only downloaded when the Co-signer goes through a setup or upgrade process, so it is omitted from the Docker image. The name of the file is `enclave.signed.so`. The chain of trust follows the sequence: **script > image > executable > enclave**, with Fireblocks ensuring compatibility between these components. During setup or upgrade, the Co-signer downloads the latest enclave version from Fireblocks servers, which is guaranteed to be compatible with the installed executable. To ensure the enclave uses the latest published version, always use the most recent Co-signer Docker image. The safest approach is to install the Co-signer using the latest installation script. # SGX API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/azure/sgx-maintenance-azure > [!NOTE] > > You must have root privileges on the Co-signer machine to perform maintenance operations. Ensure you are logged in as a root user or use `sudo` to execute the commands. ## View the logs You can export the logs to a file in the local directory, tagged with the current date and time, by running the following command: ```bash theme={"system"} ./cosigner logs ``` Append a number to the command to retrieve the specified most recent amount of lines. By default, the Co-signer uses the Docker logging container. However, for better monitoring, the logs can be streamed to an external log delivery service, such as ELK, Splunk, or Datadog. The SGX Co-signer generates two different types of logs for troubleshooting and auditing: * `run.log` is an automatically generated log containing all of your Co-signer setup and configuration history. * `customer_cosigner.log` contains workspace operation data, such as the workspace's transaction approving and signing history and new connection approvals. These log files are separated into 10 files limited to 4.1MB each. They are automatically overwritten in chronological order. Your API Co-signer logs are structured using the following Regular Expression (RegEx): ```bash theme={"system"} ${AppName}:${Line} ${LogLevel} ${dd/MM/yyyy} ${HH:mm:ss,fff} ${ClassName} ::${MethodName} - ${LogMessage} ``` These logs are collected by the Docker container’s `STDOUT` stream and written into a log file on the container’s host machine at the location specified below. These log files are rotated with a max file size of 4MB for each log file. ```text theme={"system"} /databases/cosigner/log/customer_cosigner.log /databases/cosigner/log/customer_cosigner.log.1 /databases/cosigner/log/customer_cosigner.log.2 ... ``` > [!NOTE] > > The Co-signer is Docker-based and uses the Docker JSON file driver for logging. By default, the driver uses a standard logging output but can be configured to support other logging mechanisms. Refer to the official Docker documentation for more detailed information. *** ## Verify SGX is enabled on the machine After completing your server creation or installation, verify that SGX is enabled on the server. The Fireblocks API Co-Signers can only run on SGX-enabled servers with the latest patches, and verifying your configuration helps ensure you do not run into any potential issues later on. 1. Run the following commands on the server: ```bash theme={"system"} sudo apt update sudo apt upgrade sudo apt install cpuid cpuid -1 | grep -i sgx ``` 2. Verify the following: 1. SGX: Software Guard Extensions supported is `true`. 2. SGX\_LC: SGX launch config supported is `true`. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/0c0a9cb9db9029ad2559a58ddcde807472c7674dc0a81d25cd6dd58f57f37c5b-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=128b5d0f685e48863e18cc368d7d140b) > [!NOTE] > > Azure Standard\_DC\*\_v2 instances do not support SGX2, but it is not necessary to run the signer. *** ## List the paired API users You can list all API users paired with the Co-signer across the connected workspaces by running the following command: ```bash theme={"system"} ./cosigner list-users ``` The output will display a list of all API users paired with your Co-Signer, including the workspace name they are connected to, the API user's ID (its API key), and the associated Callback Handler server URL (if applicable). *** ## Retrieve the public key You can retrieve the Co-signer's public key, used by your optional Callback Handler server to authenticate requests from the Co-signer, by running the following command: ```bash theme={"system"} ./cosigner print-public-key ``` *** ## Stop the Co-signer You can stop the Co-signer by running the following command: ```bash theme={"system"} ./cosigner stop ``` *** ## Start the Co-signer You can Start the Co-signer by running the following command: ```bash theme={"system"} ./cosigner start ``` *** ## Restart the Co-Signer You can restart the Co-signer by running the following command: ```bash theme={"system"} ./cosigner restart ``` *** ## Retrieve the running image version You can retrieve your running Co-Signer version by running the following command: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image' + " " + '.[0].Image' ``` *** ## Retrieve the script version You can retrieve your current Co-Signer script version by running the following command: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` *** ## Update the Co-signer In the Fireblocks Console, go to [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer) and copy the download link of the Intel SGX installation script. The link is valid for seven days. Use the `curl` command to download the script to the path where the existing Co-Signer script is present. Paste the copied URL into the following command: ```bash theme={"system"} curl -o cosigner "URL Download Link" ``` > [!NOTE] > > If you have any issues with finding the installation script URL, please contact [Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=360003372200\&tf_360023089139=global_settings\&tf_360023089159=get_api_co-signer_installation_script). After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` Stop the Co-signer container by running the following command: ```bash theme={"system"} ./cosigner stop ``` Run `docker ps -a` and check the output. There should be no Co-signer container running. Backup the Co-signer's database by running the following command: ```bash theme={"system"} mv /databases/cosigner/enclave/enclave.signed.so /databases/cosigner/enclave/enclave.signed.so_old ``` Remove the current revision file: ```bash theme={"system"} sudo rm -f .revision ``` You can now update the Co-signer to the latest version by running the following command: ```bash theme={"system"} ./cosigner start ``` You can update to a specific image version by replacing the `` : ```bash theme={"system"} ./cosigner start ``` Run `docker ps -a` and check the output. You should see the Co-signer container running. If the update process did not succeed, try re-installing the Co-signer using the same version, but first remove the current version of the Co-Signer by running the following commands: ```bash theme={"system"} sudo rm -rf /databases sudo rm -f .local_config .revision ``` and then run: ```bash theme={"system"} ./cosigner setup ``` If that still doesn't work, refer to [API Co-signer Troubleshooting](/cosigners/operations/api-cosigner-troubleshooting). *** ## Migrate to a new machine Begin by setting up a new SGX-capable machine in your cloud or on-premises environment. Migrating the SGX Co-Signer to a new machine is most effectively performed using backup data from the existing machine. This approach minimizes downtime and reduces the risk of interruptions to your workspace operations. The following files and folders are generated automatically during the SGX Co-signer setup and are essential for backup and recovery purposes: 1. `/databases/cosigner/db`: The Co-signer DB folder stores the `secrets.db` file, the Co-Signer's secrets database. This file is encrypted with a key generated in a secure SGX enclave. 2. `/databases/cosigner/backup`: The backup folder maintained by the Co-Signer stores the backup upon any change to the database. The files are formatted using the following timestamp: `%Y%m%d%H%M%S.db` (e.g. `20220710184350.db`) based on the backup's date. 3. `/databases/cosigner/enclave/ra_loader_enclave.signed.so` - The enclave loader. Backup the files `secrets.db` and `ra_loader_enclave.signed.so` and copy them to the same folder locations on the new server: * `/databases/cosigner/db/secrets.db` * `/databases/cosigner/enclave/ra_loader_enclave.signed.so` After the above files have been copied to the new server, run `./cosigner start`. If you don't have backups of either `secrets.db` or `ra_loader_enclave.signed.so` files, unpair (re-enroll) the API users that are associated with the Co-signer and set up a new Co-signer on the new machine. Install the replacement on [Azure](/cosigners/azure/install-api-cosigner-azure), [Azure Marketplace](/cosigners/azure/install-api-cosigner-azure-marketplace), [IBM Cloud](/cosigners/ibm/install-api-cosigner-ibm), [Alibaba Cloud](/cosigners/alibaba/install-api-cosigner-alibaba), or [on-premises](/cosigners/on-premises/install-api-cosigner-onprem). # SGX API Co-signer Version History Source: https://developers.fireblocks.com/cosigners/azure/sgx-versions-azure Fireblocks updates the API Co-signer software to include stability enhancements and new features. This article provides details about the most recent SGX Co-signer versions. Always use the latest version listed here for optimal performance. ## Latest SGX Co-signer image version ### **Version 2026.10.21 (October 2026)** **Version hash:** `90324e06` * Callback enhancement: the number of change outputs is now included in the transaction metadata sent to the callback handler * Base image and root CA updates * Security fixes and updates * Support RHEL 9 and Ubuntu 26.04 hosts *** ## SGX Co-signer image version history ### **Version 2026.10.03 (October 2026)** **Version hash:** `77774ae4` * Security and bug fixes ### **Version 2026.09.16 (September 2026)** **Version hash:** `0449f81f` * Fast signing for EdDSA * Base image updated * Security updates ### **Version 2026.08.19 (August 2026)** **Version hash:** `f5ab4d7f80` * Callback responses may now be signed with ECDSA P-256 (ES256) in addition to RSA (RS256) * Security fixes ### **Version 2026.07.20 (July 2026)** **Version hash:** `86d1be9f9b` * Source and destination tags are now sent to the callback handler * Security improvements and bug fixes ### **Version 2026.06.24 (June 2026)** **Version hash:** `80e34d008a` * Performance improvements and bug fixes ### **Version 2026.05.27 (May 2026)** **Version hash:** `1b31e6b7fb` * Support for Fireblocks BCM * Support for batch attachment of tags * Performance improvements and bug fixes ### **Version 2026.04.27 (April 2026)** **Version hash:** `1d5b48eeef` * Performance improvements, security enhancements and bug fixes ### Version 2026.03.02 (March 2026) **Version hash:** 95a317d3c7 * Added support for POLICY\_CHANGE approval request type (the customer co-signer can now approve policy changes via Policy Service v2) * Performance improvements and bug fixes ### Version 2026.01.29 (January 2026) **Version hash:** 340d9f6057 * Bug fixes and performance improvements ### Version 2025.12.30 (December 2025) **Version hash:** a294449c1b * Added support for "Protected Tags" approval requests ### Version 2025.12.11 (December 2025) **Version hash:** c44604398e * The callback handler can now authenticate using a certificate signed by a root CA certificate preloaded to the Co-signer * The callback handler can now authenticate using both JWT and a certificate provided directly to the Co-signer (or signed by a root CA certificate provided to the Co-signer) * The callback can now return a "retry" response on exchange withdraw operation ### Version 2025.10.09 (October 2025) **Version hash:** 4e34888c01 * Fixed bug parsing messages after websocket reconnect. * Fixed bug sending messages on half-closed websocket connections. ### Version 2025.08.12 (August 2025; US environments only) **Version hash:** 19c07421dc * Fixed WebSocket bug causing performance issues ### Version 2025.07.16 (July 2025) **Version hash:** bb61e507 * Fixed bugs and increased performance * Better support for web sockets ### Version 3.10.1 (April 2025) **Version hash:** 7160d7b833 * Bug fixes and performance improvements ### Version 3.7.1 (October 2024) **Version hash:** 4ec196aec5 * Decreased signing latency, along with general performance increases * Remote management capabilities through Fireblocks Console * Support for more types of approval requests * Various fixes and improved handling of internal errors * Timing details are logged after every outgoing network request * The cosigner version will be reflected in the HTTP User-Agent header ### Version 3.6.5 (February 2024) **Version hash:** 3b807e2af7 * Improved logging for easier problem-solving * Improved stability, security, and performance * Enhanced deployment flexibility * Added support for the Callback Handler retry mechanism, enabling the Callback Handler to respond asynchronously * Updated major and minor dependencies * Preliminary support for upcoming features * Bug fixes ### Version 3.5.0 (August 2023) * [MPC-CMP security enhancements](https://support.fireblocks.io/hc/en-us/articles/10167020461596-MPC-CMP-security-enhancements-version-update-August-2023) * Bug fixes ### Version 3.3.0 (January 2022) * Added support for HTTP network proxy on outbound requests * Bug fixes *** ## Latest SGX Co-signer script version ### Version 2025.10.11 (December 2025) * New default Docker image: 2025.10.11 ## SGX Co-Signer script version history ### Version 2025.10.09 (October 2025) * New default Docker image: 2025.10.09 * Switched to Calendar Versioning to align script versions with default image versions ### Version 1.1.13 (August 2025) * New default Docker image 2025.08.12 ### Version 1.1.11 (April 2025) * New default Docker image 3.10.1 ### Version 1.1.10 (January 2025) * New default Docker image 3.7.1 ### Version 1.1.9 (February 2024) * New default Docker image 3.6.5 ### Version 1.1.8 (January 2024) * Added support for Callback Handler configuration via Azure Marketplace * Same default Docker image as in image version 3.5.0 and used as in script version 1.1.7 ### Version 1.1.7 (August 2023) * New default Docker image 3.5.0 ### Version 1.1.6 (February 2023) * Added support for Docker version 1.29.2 * Added validation for microcode: * Additional information about the microcode version appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem * Added validation to make sure Hyper-Threading is disabled * If Hyper-Threading is enabled, additional information appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem ### Version 1.1.5 (February 2022) * New default Docker image 3.3.0 * Added support for Co-signers in high-availability * Updated authentication model * Added support for HTTP network proxy on outbound requests *** ## Checking SGX Co-signer versions ### How do I check the version of my running SGX Co-signer image? To check your running SGX Co-signer image version, run the following command on your VM: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image +" " + . [0].Image' ``` ### How do I check the version of my running SGX Co-signer script? To check your running SGX Co-signer script version, run the following command on your VM: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` # Approve configuration changes Source: https://developers.fireblocks.com/cosigners/callback-handler/approve-configuration-changes ## Configuration Approval Callback `POST /v2/config_change_sign_request` This request expects a [CallbackResponse](/cosigners/callback-handler/response-object) object from the callback handler. If the callback handler does not respond within 30 seconds, Fireblocks fails the request. If your callback handler can't respond within 30 seconds, you can use the [retry mechanism](/cosigners/callback-handler/response-object) by responding with `RETRY`. *** ## Request parameters **requestId** `string` A unique identifier of this request. It must be returned in the response. **signerId** `string` (Optional) The Fireblocks API user ID associated with the cosigner device that's signing this approval. **type** `string` The type of configuration request. See [Configuration types](#configuration-types) for the full list of values and their fields. **extraInfo** `object` Additional information about the request. The shape depends on `type`. See [Configuration types](#configuration-types). *** ## Configuration types Each type below lists the fields Fireblocks sends in `extraInfo` for that specific configuration change. ### Wallets and external accounts #### UNMANAGED\_WALLET An event that involves address whitelisting. This type also covers the `UNMANAGED_V2` internal request type — both surface as `UNMANAGED_WALLET` in the callback. **subType** `string` `INTERNAL` - Internal Wallets are addresses you control outside your Fireblocks workspace. Internal addresses display their current balance and are included in your workspace's total billable address count. `EXTERNAL` - External Wallets are addresses managed by your clients and counterparties. `CONTRACT` - Contract Wallets are addresses of smart contracts you want to interact with. Currently, this only applies to smart contracts on EVM-compatible blockchains. **walletName** `string` The name of the internal, external, or contract wallet you want to approve adding an address to. **walletId** `string` The ID of the internal, external, or contract wallet that you want to approve adding an address to. **asset** `string` The ID of the asset to add to this wallet. Use [GET supported assets](/api-reference/blockchains-&-assets/list-assets-legacy) request to retrieve more information about an asset. **address** `string` The asset deposit address requested to be added to the wallet. **tag** `string` Destination address tag for Ripple; destination memo for EOS, Stellar, Hedera, & DigitalBits; destination note for Algorand; bank transfer description for fiat providers. **Note:** For Stellar, the memo must be a string representation of an integer between "0" and "2147483647". Setting the memo to other values for Stellar assets will result in a failed request with an error message. **additionalInfo** `object` (Optional) Extra address metadata, present only when the whitelisting request originated from certain workspace flows. #### UNMANAGED\_DAPP An event that involves adding an unmanaged dApp connection to your workspace. **dappContainerName** `string` The name of the dApp connection's wallet container. **walletContainerId** `string` The ID of the dApp connection's wallet container. **dappName** `string` The name of the dApp. **dappUrl** `string` The URL of the dApp. **subType** `string` The dApp connection subtype. #### EXCHANGE, FIAT\_ACCOUNT, and CONNECTED\_ACCOUNT An event that involves adding an exchange account, fiat account, or connected account to your workspace. All three types share the same fields. **subType** `string` The specific exchange, fiat account, or connected account provider. **accountName** `string` The account's name. **accountId** `string` The account's ID. **apiKey** `string` The third-party service's API key. Blank for certain provider subtypes. **addresses** `string` (Optional) A JSON-formatted "key":"value" string, where "key" is an asset symbol and "value" is its address. There can be multiple entries in the JSON. Only populated for `FIAT_ACCOUNT`. Use [supported\_assets](/api-reference/blockchains-&-assets/list-assets-legacy) to retrieve asset symbols. ### Users and devices #### ADD\_USER An event that involves adding a user to your workspace. **userId** `string` The UUID of the user being added. **type** `string` The type of the added user: `CONSOLE` or `API`. **role** `string` The user's assigned role. **fullName** `string` The user's first and last name. **email** `string` The user's email address. #### DELETE\_USER An event that involves removing a user from your workspace. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. **id** `string` The ID of the user being removed. **firstName** `string` The user's first name. **lastName** `string` The user's last name. **email** `string` The user's email address. **role** `string` The user's role. **alerts** `string` A newline-separated list of alerts related to deleting this user (for example, pending requests or owned resources), if any. #### EDIT\_USER\_INFO An event that involves editing a user's name, email, or role. **beforeChangedFirstName** `string` The user's first name before the change. **beforeChangedLastName** `string` The user's last name before the change. **beforeChangedEmail** `string` The user's email before the change. **beforeChangedRole** `string` The user's role before the change. **requestedFirstName** `string` The requested first name. **requestedLastName** `string` The requested last name. **requestedEmail** `string` The requested email. **requestedRole** `string` The requested role. #### RE\_ENROLL\_DEVICE An event where an admin re-enrolls a mobile device for one of the users. Unlike `ADD_USER`, this type has no CONSOLE/API field: the `role` value here is deliberately stripped of the `API USER:` prefix, so you can't recover whether the user is a console or API user from this payload. **userId** `string` The UUID of the user whose device is being reset. **role** `string` The user's role, without any console/API user prefix. **fullName** `string` The user's first and last name. **email** `string` The user's email address. ### Cosigner and device pairing #### COSIGNER\_CHANGE\_CALLBACK and PAIR\_VIRTUAL\_DEVICE `COSIGNER_CHANGE_CALLBACK` changes the callback configuration (URL, certificate, JWT public key) of a device that's already paired. `PAIR_VIRTUAL_DEVICE` pairs a new virtual (non-mobile) device using a pairing token. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **userId** `string` The ID of the API user being paired. **userType** `string` The type of the paired user: `CONSOLE` or `API`. **name** `string` The paired user's first name. **role** `string` The paired user's role, including any console/API user prefix. **pairingToken** `string` Always `null` in this payload for both types. The actual pairing token is generated after approval and delivered separately. **customerServerURL** `string` Your customer server URL to pair with. **customerServerCert** `string` Your customer server certificate. **customerJWTPubkey** `string` Your customer JWT public key. **physicalDeviceId** `string` The physical device identifier. **deviceId** `string` The device identifier. **requestId** `string` Always `null` in this payload for both types. Use the callback's top-level `requestId` instead. ### Quorum and MPC configuration #### CHANGE\_QUORUM\_THRESHOLD An event where the admin updates the quorum threshold. **oldThreshold** `number` The quorum threshold before the change. **newThreshold** `number` The requested quorum threshold. #### CONFIGURE\_OWNER\_IN\_ADMIN\_QUORUM An event that changes whether the workspace owner counts toward the admin quorum. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **ownerInQuorum** `boolean` Whether the workspace owner counts toward the admin quorum. #### SET\_MPC\_PROVISIONER An event that designates a user as an MPC provisioner. **userId** `string` Always `null` in this payload — no producer currently populates it. **isMPCProvisioner** `boolean` Whether the user is set as an MPC provisioner. ### IP allow list #### ACTIVATE\_IP\_ALLOW\_LIST An event that turns the IP allow list on or off. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. **activate** `boolean` Whether the IP allow list is being turned on or off. **ruleId** `string` The ID of the allow list rule. #### ADD\_IP\_ADDRESS An event that adds an IP address to the allow list. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. **name** `string` The label for this IP address entry. **ipAddress** `string` The IP address being added. **id** `string` The ID of this IP address entry. **ruleId** `string` The ID of the allow list rule. **createdAt** `string` When this entry was created. **updatedAt** `string` When this entry was last updated. #### DELETE\_IP\_ADDRESS An event that removes an IP address from the allow list. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. **name** `string` The label for this IP address entry. **ipAddress** `string` The IP address being removed. **id** `string` The ID of this IP address entry. **ruleId** `string` The ID of the allow list rule. **createdAt** `string` When this entry was created. **updatedAt** `string` When this entry was last updated. **ipId** `string` The ID of the removed IP address. #### UPDATE\_IP\_ADDRESS An event that updates an existing IP address on the allow list. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. **name** `string` The current label for this IP address entry. **newName** `string` The requested label for this IP address entry. **ipAddress** `string` The current IP address. **newAddress** `string` The requested IP address. **id** `string` The ID of this IP address entry. **ruleId** `string` The ID of the allow list rule. **createdAt** `string` When this entry was created. **updatedAt** `string` When this entry was last updated. **ipId** `string` The ID of the updated IP address. ### dApp connections and MEV protection #### ENABLE\_ONE\_TIME\_ADDRESS An event which involves enabling a particular transaction to a one-time address in the workspace. This type has no `extraInfo` fields. #### ENABLE\_NON\_EVM\_DAPPS\_CONNECTIONS An event that enables dApp connections for non-EVM blockchains. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. #### ENABLE\_MEV\_DAPPS, DISABLE\_MEV\_DAPPS, ENABLE\_MEV\_API, and DISABLE\_MEV\_API An event that enables or disables MEV protection for dApp connections or API-initiated transactions. All four types share the same fields. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. ### Fireblocks Network #### ADD\_NETWORK\_CONNECTION An event of adding a Fireblocks Network new connection. **networkConnectionId** `string` ID of network connection **note** `string` Connection note **localNetworkId** `string` ID of local networkId **remoteNetworkId** `string` ID of the remote peer's networkId **remoteTenantId** `string` ID of remote peer tenantId **routingPolicy** `string` (Optional) JSON representation of routing policy object. Only included in the payload sent to the connection's initiating tenant, not the receiving tenant. #### SET\_NETWORK\_CONNECTION\_ROUTING\_POLICY An event where the admin configures the routing policy for each network connection. **connectionId** `string` ID of network connection **routingPolicy** `string` JSON representation of routing policy object #### SET\_NETWORK\_ID\_POLICY An event of setting up the Fireblocks Network *Network ID* in case its profile is not discoverable. **networkId** `string` ID of networkID **routingPolicy** `string` JSON representation of routing policy object ### Policies and approval groups #### POLICY\_APPROVAL An event that involves updating the Transaction Authorization Policy. **origEditorId** `string` The ID of the user who originally edited the policy. **origEditor** `string` The name of the user who originally edited the policy. **editorId** `string` The ID of the user who most recently edited the policy. **editor** `string` The name of the user who most recently edited the policy. **editTime** `number` When the policy was edited, in Unix epoch time. **creator** `string` The ID of the user who initiated the request. **publisher** `string` The name of the user who published the policy. **publishTime** `number` When the policy was published, in Unix epoch time. **allowRules** `number` (Optional) The number of allow rules in the policy. **blockRules** `number` (Optional) The number of block rules in the policy. **twoTierRules** `number` (Optional) The number of two-tier approval rules in the policy. #### POLICY\_CHANGE An event that involves a pending edit to the Transaction Authorization Policy before it's published. **origEditorId** `string` The ID of the user who originally edited the policy. **origEditor** `string` The name of the user who originally edited the policy. **editorId** `string` The ID of the user who most recently edited the policy. **editor** `string` The name of the user who most recently edited the policy. **editTime** `number` When the policy was edited, in Unix epoch time. **creator** `string` The ID of the user who initiated the request. **publisher** `string` The name of the user who published the policy. **publishTime** `number` When the policy was published, in Unix epoch time. **actionCounters** `object` (Optional) A summary of pending rule changes. Keys combine the rule type and verdict action (for example, `transferALLOWRules`); values are display strings such as `"3 Rules"`, not numeric counts. For a change spanning multiple policies, only the last policy's counters are present. #### UPDATE\_APPROVAL\_GROUP\_MAPPING An event involving gathering a list of approvers for a particular admin operation. **creatorFirstName** `string` The first name of the user who initiated the request. **creatorLastName** `string` The last name of the user who initiated the request. **ccrType** `string` The admin operation type this approval group mapping applies to. **isOwnerMandatory** `boolean` Whether the workspace owner must be included in the approval group. **newGroupId** `string` The ID of the requested approval group. **newThreshold** `number` The requested approval threshold. **newGroupName** `string` The name of the requested approval group. **groupMembersCount** `number` The number of members in the requested approval group. **timestamp** `number` When the request was created, in Unix epoch seconds. #### USERS\_GROUP\_APPROVAL An event that involves creating, editing, or deleting a user group in your workspace. The fields included depend on the `subType`. **subType** `string` `CREATE_USERS_GROUP`, `EDIT_USERS_GROUP`, or `DELETE_USERS_GROUP`. **creator** `string` The ID of the user who initiated the request. **groupId** `string` The ID of the user group. **groupName** `string` The name of the user group. **pendingChanges** `array` A list of the pending changes to the group. **entry object** **type** `string` `RENAMED_TITLE`, `ADDED_USER`, `REMOVED_USER`, or `DELETE_GROUP`. **title** `string` (Optional) The new group name. Present when `type` is `RENAMED_TITLE`. **userId** `string` (Optional) The affected user's ID. Present when `type` is `ADDED_USER` or `REMOVED_USER`. **membersSummary** `array` A breakdown of the group's members by role, plus a `total` entry. **entry object** **key** `string` A role name, or `total` for the overall member count. **value** `number` The number of members for this `key`. **newGroupName** `string` (Optional) The requested group name. Included when `subType` is `EDIT_USERS_GROUP`. **addedUsers** `array` (Optional) Users added to the group. Included when `subType` is `CREATE_USERS_GROUP` or `EDIT_USERS_GROUP`. **entry object** **fullName** `string` The user's first and last name. **role** `string` The user's role. **removedUsers** `array` (Optional) Users removed from the group. Included when `subType` is `EDIT_USERS_GROUP`. Same entry shape as `addedUsers`. **membersIds** `array` (Optional) The IDs of the group's members, as strings. Included when `subType` is `CREATE_USERS_GROUP` or `EDIT_USERS_GROUP`. **groupMembers** `array` (Optional) The group's members. Included when `subType` is `DELETE_USERS_GROUP`. Same entry shape as `addedUsers`. #### REGISTER\_EXTERNAL\_KEYS\_VALIDATOR\_KEY An event that registers a validator key for External Keys. **creatorFirstName** `string` The first name of the user who initiated the request. **creatorLastName** `string` The last name of the user who initiated the request. **validatorKey** `string` The validator key being registered. ### Automation rules #### ADD\_ACTIVE\_AUTOMATION\_RULE, ADD\_INACTIVE\_AUTOMATION\_RULE, EDIT\_AUTOMATION\_RULE, DELETE\_AUTOMATION\_RULE, ACTIVATE\_AUTOMATION\_RULE, and DEACTIVATE\_AUTOMATION\_RULE An event that adds, edits, deletes, activates, or deactivates an automation rule. All six types share the same fields. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **type** `string` The configuration request type. **request** `object` The full automation rule definition submitted for this change. Its schema is defined by the Automation Rules API. ### Protected tags Editing, deleting, attaching, and detaching a [protected tag](/docs/tags#protected-tags-and-the-approval-flow) requires quorum approval. These events carry those changes. #### EDIT\_PROTECTED\_TAG An event that edits a protected tag. **tagId** `string` The ID of the protected tag. **creator** `string` The ID of the user who initiated the request. **operationData** `object` The requested changes to the tag, as `newTag` and `previousTag` objects with the same shape. **properties** **newTag** `object` The tag's state after the edit. **properties** **tagId** `string` The tag's ID. **label** `string` The tag's label. **color** `string` The tag's color. **description** `string` The tag's description. **isProtected** `boolean` Whether the tag is protected. **tagType** `string` The tag's type. **previousTag** `object` The tag's state before the edit. Same fields as `newTag`. #### DELETE\_PROTECTED\_TAG An event that deletes a protected tag. **tagId** `string` The ID of the protected tag. **creator** `string` The ID of the user who initiated the request. **protectedTag** `object` (Optional) The tag being deleted. **properties** **label** `string` The tag's label. **description** `string` The tag's description. **color** `string` The tag's color. **tagType** `string` The tag's type. **typeName** `string` The tag type's display name. #### ATTACH\_AND\_DETACH\_PROTECTED\_TAGS An event that attaches or detaches protected tags on one or more entities. **creator** `string` The ID of the user who initiated the request. **entities** `array` (Optional) The entities being tagged or untagged. **entry object** **text** `string` The entity's display name. **id** `string` The entity's ID. **attachedTags** `array` (Optional) The tags being attached. **entry object** **label** `string` The tag's label. **description** `string` The tag's description. **color** `string` The tag's color. **tagId** `string` The tag's ID. **tagType** `string` The tag's type. **detachedTags** `array` (Optional) The tags being detached. Same entry shape as `attachedTags`. #### CREATE\_ENTITIES\_WITH\_TAG\_ATTACHMENTS An event that creates entities with protected tags attached. **creator** `string` The ID of the user who initiated the request. **tenantId** `string` The workspace ID. **tagIds** `array` (Optional) The IDs of the tags being attached. **entityType** `number` (Optional) The type of entity being created: `1` for a vault account, `2` for a contact. **entityCount** `number` (Optional) The number of entities being created. **maxEntityId** `number` (Optional) The highest existing vault account ID at request time, used as a floor for the newly created IDs. Only present when `entityType` is `1` (vault account). **tags** `object` (Optional) The tags being attached. Unlike `ATTACH_AND_DETACH_PROTECTED_TAGS`, this field isn't unwrapped to a plain array before being sent — the tag list is nested under `values`. **properties** **arrayTitle** `object` A display label for this list, for Fireblocks' own UI. Not something a callback handler needs to act on. **properties** **singular** `string` Singular form of the label. Always `"Tag"`. **plural** `string` Plural form of the label. Always `"Tags"`. **values** `array` The tags being attached. **tag object** **label** `string` The tag's label. **description** `string` The tag's description. **color** `string` The tag's color. **tagId** `string` The tag's ID. **tagType** `string` The tag's type. # Approve transactions Source: https://developers.fireblocks.com/cosigners/callback-handler/approve-transactions ## Transaction Signing Callback Handler `POST /v2/tx_sign_request` This request is used for transaction signing and approval and expects a [CallbackResponse](/cosigners/callback-handler/response-object) object from the callback handler. `IGNORE` applies to approval requests only. If the callback handler does not respond within 30 seconds, Fireblocks fails the request. If your callback handler can't respond within 30 seconds, you can use the [retry mechanism](/cosigners/callback-handler/response-object) by responding with `RETRY`. If both approval and signing are requested for a transaction, one callback handles both requests, but the two stages don't send an identical payload. Fields below that only appear in one stage are tagged **Approval only** or **Signing only**; untagged fields are sent in both. *** ## Request parameters ### Identifiers **txId** `string` The transaction's internal ID in Fireblocks. **requestId** `string` A unique identifier of this request is returned in the response. For a batched transaction, this retains the batch suffix that `txId` does not. **externalTxId** `string` An optional but highly recommended parameter. Fireblocks will reject future transactions with same ID. You should set this to a unique ID representing the transaction, to avoid submitting the same transaction twice. This helps with cases where submitting the transaction responds with an error code due to Internet interruptions, but the transaction was actually sent and processed. To validate whether a transaction has been processed, Find a specific transaction by external transaction ID. There is no specific format required for this parameter. ### Signers **signerId** `string` (Optional) The Fireblocks API user ID associated with the cosigner device that's signing. This identifies the cosigner, not necessarily the person who created the transaction. **players** `array` A list of the Co-signers that signed the transaction. (Two Fireblocks SaaS and at least one user device or one API Co-signer). Each signer is represented by a Device ID. ### Transaction details **operation** `string` The [transaction operation](#transactionoperation) type. The default is `TRANSFER`. **asset** `string` The asset ID in Fireblocks. **amount** `number` *deprecated* Use the **amountStr** field for accuracy. If the transfer is a withdrawal from an exchange, the actual transfer amount. Otherwise, the requested amount. **amountStr** `string` The amount of the transfer in string format. **requestedAmount** `number` *deprecated* Use the **requestedAmountStr** field for accuracy. The requested transfer amount. In gross transactions, transaction fees are deducted from this amount. **requestedAmountStr** `string` The requested transfer amount in string format. In gross transactions, transaction fees are deducted from this amount. **fee** `string` The transaction's estimated fee. Not included in the approval-stage payload at all. **changeIndex** `number` (Optional) The change output index, for UTXO-based blockchains that return change to a Fireblocks address. **note** `string` (Optional) Custom note that describes this transaction in your Fireblocks workspace. The note isn't sent to the blockchain. **extraParameters** `object` (Optional) Parameters that are specific to some transaction operation types and blockchain networks. Learn more [below](#transactionextraparameters). **rawTx** `array` An array of [RawTX](#rawtx) objects. Contains a list of the actual transaction payload sent to the blockchain. Note that some signing requests represent multiple transactions. When this occurs, the list contains more than one object. This parameter is not included in the CallbackResponse object when using a Fireblocks EU cloud environment. This field is included for the following blockchains: ADA, BTC, DASH, DOGE, LTC, BSV, BCH, XEC, ZEC, EVMs, all Cosmos based chains, SOL, XLM, XTZ, TRX, NEAR, XRP and TON. This is an opt-in feature. Please contact [Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new) to include this feature in your workspace. ### Source and destination **sourceType** `string` The [source](/reference/transaction-sources-destinations) of the transaction. **sourceId** `string` The transaction's source vault account ID or exchange account UUID. **destType** `string` The transaction's destination type: `VAULT`, `EXCHANGE`, `ONE_TIME`, `UNMANAGED`, `NETWORK_CONNECTION`, `COMPOUND`, `NONE`, `OEC_PARTNER`, `END_USER_WALLET`, `GLOBAL_WHITELIST`, `CONNECTED_ACCOUNT`, or `LEGACY_TO_NEW_VAULT`. **destId** `string` The destination's vault account ID or exchange account UUID. **destAddress** `string` The destination address of the transaction. Always present at the signing stage; only present at the approval stage when the destination has a resolvable address. **destAddressType** `string` The destination's address type: `WHITELISTED` (default) or `ONE_TIME`. Not included in the approval-stage payload. **destinations** `array` An array of [TransactionRequestCallbackDestination](#transactionrequestcallbackdestination) objects with all details for all destination(s). ### Virtual sub-accounts Present only at the approval stage, only when the transaction's source or destination is a virtual sub-account of a vault account. Not included in the signing-stage payload. **virtualSrcType** `string` The virtual (sub-account) type of the transaction source. **virtualSrcId** `string` The virtual (sub-account) identifier of the transaction source. **virtualDstType** `string` The virtual (sub-account) type of the transaction destination. **virtualDstId** `string` The virtual (sub-account) identifier of the transaction destination. ### Trading account and third-party routing Present only at the approval stage, when the transaction has associated Fireblocks Network routing metadata. Not included in the signing-stage payload. **isThirdPartyRouting** `boolean` Present whenever network-routing metadata exists on the transaction; `true` when that routing is through a third party. **underlyingSrcType** `string` (Optional) The type of the underlying source account behind the third-party routing. **underlyingSrcId** `string` (Optional) The identifier of the underlying source account behind the third-party routing. **srcTradingAccount** `string` (Optional) The off-exchange trading account associated with the transaction source. **dstTradingAccount** `string` (Optional) The off-exchange trading account associated with the transaction destination. ### Protected tags Only [protected tags](/docs/tags#protected-tags-and-the-approval-flow) reach the callback. Standard tags are not included. Use them to apply different signing logic to tagged vault accounts. **sourceTags** `array` (Optional) A list of protected tags attached to the transaction source. **entry object** **tagId** `string` The tag's ID. **label** `string` The tag's label. **color** `string` (Optional) The tag's color. **description** `string` (Optional) The tag's description. **tagType** `string` (Optional) The tag's type. *** ## TransactionOperation **operation** `string` `TRANSFER`: Default. Transfers funds from one account to another. UTXO blockchains allow multi-input and multi-output transfers. All other blockchains allow transfers with one source address and one destination address. `MINT`: Mints new tokens. Supported for Stellar, Ripple, and EVM-based blockchains. `BURN`: Burns tokens. Supported for Stellar, Ripple, and EVM-based blockchains. `CONTRACT_CALL`: Calls a smart contract method for web3 operations on any EVM blockchain. The [Fireblocks development libraries](/reference/evm-web3-provider#convenience-libraries) are recommended for building contract call transactions. `TYPED_MESSAGE`: An off-chain message in either Ethereum Personal Message or EIP712 format. Use it to sign specific readable messages that are not actual transactions. [Learn more about typed messages](/docs/typed-message-signing-1). `RAW`: An off-chain message with no predefined format. Use it to sign any message with your private key, including protocols such as blockchains and custom transaction types that are not natively supported by Fireblocks. [Learn more about raw signing transactions](/docs/raw-signing). `ENABLE_ASSET`: Algorand, DigitalBits, Solana, and Stellar require an on-chain transaction to create an asset wallet and enable the deposit address. This transaction is automatically created when adding assets on these blockchains to a vault account. `STAKE`: Assign assets to a staking pool managed by a staking validator. [Learn more about staking transactions and supported blockchains](/reference/staking-overview). This transaction is automatically created when performing staking operations. `SUPPLY`, `REDEEM`, `APPROVE`, `ENTER_MARKET`, `EXIT_MARKET`: DeFi lending-protocol operations. `DEPLOYMENT`: Deploys a smart contract. `PROGRAM_CALL`: Calls a Solana program. `UPGRADE`: Upgrades a deployed program or contract. `OFFER_RESPONSE`: Responds to a trading offer. ### TransactionExtraParameters **inputsSelection** `object` For UTXO-based blockchain multi-input selection, use the `inputsSelection` field with values set to the [InputsSelection object](/reference/transaction-objects#inputsselection). The inputs can be retrieved using the Retrieve Unspent Inputs endpoint. **rawMessageData** `object` For `RAW` operations, use the rawMessageData field with the values set to the [RawMessageData object](/reference/raw-signing-objects#rawmessagedata). **contractCallData** `string` For `CONTRACT_CALL` operations, use the contractCallData field with the value set to the Ethereum smart contract Application Binary Interface (ABI) payload. The [Fireblocks development libraries](/reference/evm-web3-provider#convenience-libraries) are recommended for building contract call transactions. *** ## TransactionRequestCallbackDestination **amountNative** `number` *deprecated* (Optional) The amount transferred to this destination as a number. Present when this destination carries its own amount metadata (multi-destination transfers). Use the amountNativeStr parameter for accurate precision. **amountNativeStr** `string` (Optional) The amount transferred to this destination represented as a string. Same presence condition as `amountNative`. **amountUSD** `number` (Optional) The USD value of the transfer to this destination. Same presence condition as `amountNative`. **originalAmount** `number` (Optional) Approval-stage only. The original (uncapped) requested amount for this destination, present only when the amount exceeds an internal display cap. **originalAmountStr** `string` (Optional) Approval-stage only. String form of `originalAmount`, under the same condition. **dstAddressType** `string` `WHITELISTED` or `ONE_TIME`. **dstId** `string` The ID of the destination. **dstName** `string` The name of the destination. **dstWalletId** `string` (Optional) The ID of the destination's wallet container, when the destination is an unmanaged or dApp wallet. **dstSubType** `string` The specific exchange, fiat account, or unmanaged wallet. For exchange accounts: `BINANCE`, `BINANCEUS`, `BITFINEX`, `BITHUMB`, `BITMEX`, `BITSO`, `BITSTAMP`, `BITTREX`, `BYBIT`, `CIRCLE`, `COINBASEEXCHANGE`, `COINBASEPRO`, `COINMETRO`, `COINSPRO`, `CRYPTOCOM`, `DERIBIT`, `GEMINI`, `HITBTC`, `HUOBI`, `INDEPENDENTRESERVE`, `KORBIT`, `KRAKEN`, `KRAKENINTL`, `KUCOIN`, `LIQUID`, `OKCOIN`, `OKEX`, `PAXOS`, `POLONIEX` For fiat accounts: `BLINC` For unmanaged wallets: `INTERNAL`, `EXTERNAL`, or `CONTRACT` **dstTag** `string` (Optional) The blockchain-level destination tag or memo for this destination (for example, a Ripple destination tag). Distinct from `tags` below, which holds protected tags, not a blockchain memo. **dstType** `string` `VAULT`, `EXCHANGE`, `ONE_TIME`, `UNMANAGED`, `NETWORK_CONNECTION`, `COMPOUND`, `NONE`, `OEC_PARTNER`, `END_USER_WALLET`, `GLOBAL_WHITELIST`, `CONNECTED_ACCOUNT`, or `LEGACY_TO_NEW_VAULT`. **displayDstAddress** `string` (Optional) The address of this specific destination. **displayDstTag** `string` (Optional) The display form of `dstTag`. **action** `string` (Optional) The policy verdict for this specific destination: `ALLOW`, `BLOCK`, `2-TIER`, `SKIP`, `BYPASS`, or `ALLOW_NO_AUTHORIZER_DEFINED`. **actionInfo** `object` (Optional) Present alongside `action`. Details about the policy rule that produced the verdict. **properties** **ruleType** `string` The type of policy that produced this verdict: `GLOBAL`, `PRE_TENANT`, or `TENANT`. **byGlobalPolicy** `boolean` Whether the matched policy is the workspace-wide global policy, as opposed to a tenant-specific one. **byRule** `boolean` Whether a specific numbered rule (rather than a policy-type-level match) produced this verdict. **capturedRuleV2** `string` Present when `byRule` is `true`. A JSON-encoded string of the matched rule, in the current (v2) policy engine's format. **capturedRuleNumV2** `string` Present when `byRule` is `true`. The matched rule's index, as a string, in the v2 policy engine. **rulesSnapshotId** `string` (Optional) Present when `byRule` is `true`. The ID of the policy snapshot the rule was matched against. **capturedRule** `string` (Optional) A JSON-encoded string of the matched rule, translated to the legacy (v1) policy format for backward compatibility. Not always present, even when `capturedRuleV2` is. **capturedRuleNum** `number` (Optional) The matched rule's index within the legacy (v1) policy array. Only present alongside `capturedRule` when a v1-equivalent rule was found. **authorizationGroups** `object` (Optional) Present only when `action` is `2-TIER`. Describes the approvers required for two-tier authorization, including a `logic` field (how the groups combine) and a `groups` array of approval groups. The exact shape varies by policy version; not fully enumerated here. **reverseAddressLookup** `object` (Optional) Present when the destination address was resolved through a reverse lookup (for example, an on-chain naming service). **properties** **resolvedAddress** `string` (Optional) The resolved destination address. **resolvedTag** `string` (Optional) The resolved destination tag. **txAdditionalDetails** `object` (Optional) Additional context about how this destination's transfer was initiated. Note the wire field name is `txAdditionalDetails`, not `additionalDetails`. **properties** **originatingSessionToken** `string` (Optional) The session token of the originating request. **partnerIntentId** `string` (Optional) The ID of the partner intent that initiated this transfer, if applicable. **programCallDecoded** `string` (Optional) The decoded Solana program call, for `PROGRAM_CALL` operations. **networkTransferInfo** `object` (Optional) Present when this destination is routed through a Fireblocks Network connection. **properties** **concreteDstId** `string` (Optional) The concrete (underlying) destination ID behind the network routing. **concreteDstType** `string` (Optional) The concrete (underlying) destination type behind the network routing. **thirdPartyInfo** `object` (Optional) Present only for third-party network transfers. **properties** **warningTitle** `string` (Optional) A warning title to display for this third-party transfer. **warningDescription** `string` (Optional) A warning description to display for this third-party transfer. **enrichmentJson** `object` (Optional) Enrichment data scoped to this specific destination. Unlike the transaction-level [trading account and third-party routing](#trading-account-and-third-party-routing) fields above (which only expose a curated subset), this is the full per-destination object, unfiltered. **properties** **underlyingInfo** `object` (Optional) **properties** **underlyingSrcType** `string` (Optional) The type of the underlying source account. **underlyingSrcId** `string` (Optional) The ID of the underlying source account. **underlyingDstType** `string` (Optional) The type of the underlying destination account. **underlyingDstId** `string` (Optional) The ID of the underlying destination account. **underlyingContext** `string` (Optional) Additional context about the underlying account relationship. **removeCollateralAddress** `string` (Optional) The address collateral is being released to, for collateral-release transfers. **removeCollateralAddressTag** `string` (Optional) The destination tag for `removeCollateralAddress`. **removeCollateralAddressSignature** `string` (Optional) A signature authorizing the collateral release. **slaInfo** `object` (Optional) Present for transfers with an SLA-based signing deadline. **properties** **slaTimeout** `number` The SLA deadline, in Unix epoch time. **slaStartTime** `number` (Optional) When the SLA period started, in Unix epoch time. **slaDeviceSigners** `array` The device IDs expected to sign within the SLA window. **oecInfo** `object` (Optional) Present for off-exchange custody transfers. **properties** **settlementId** `string` (Optional) The off-exchange settlement ID. **wallet\_connect\_service** `object` (Optional) Present for WalletConnect-initiated transfers. Note the wire field name is `wallet_connect_service`, not camelCase like most other fields in this payload. **properties** **dappUrl** `string` (Optional) The URL of the connected dApp. **pairingTopic** `string` (Optional) The WalletConnect pairing topic. **srcTradingAccount** `string` (Optional) The off-exchange trading account associated with the source, scoped to this destination. **dstTradingAccount** `string` (Optional) The off-exchange trading account associated with this destination. **swapId** `string` (Optional) The ID of the swap this transfer is part of, if applicable. **providerRampId** `string` (Optional) The ID of the fiat on/off-ramp provider transaction, if applicable. **rampPaymentTx** `object` (Optional) Present for on/off-ramp payment transfers. **properties** **paymentId** `string` The ramp provider's payment ID. **transactionId** `string` The ramp provider's transaction ID. **messageType** `number` The ramp payment message type. **esSignature** `string` The signature over the ramp payment message. **signedAtMs** `number` When the ramp payment message was signed, in Unix epoch milliseconds. **tags** `array` (Optional) A list of protected tags attached to this destination — the destination-side counterpart to the top-level `sourceTags`. **entry object** **tagId** `string` The tag's ID. **label** `string` The tag's label. **color** `string` (Optional) The tag's color. **description** `string` (Optional) The tag's description. **tagType** `string` (Optional) The tag's type. *** ## RawTX **rawTx** `string` Hex-encoded details of a transaction sent to the blockchain. **keyDerivationPath** `string` Location of the encryption key within the customer's [HD Wallet URL](https://support.fireblocks.io/hc/en-us/articles/360014330819-Fireblocks-Vault-HD-Derivation-Paths) used to sign this transaction. **payload** `string` The signing payload (typically a transaction hash) computed from `rawTx`. *** # Basic Callback Handler example Source: https://developers.fireblocks.com/cosigners/callback-handler/basic-code-example ## Request payload example This example demonstrates the raw and decoded JWT payload sent by the Co-Signer to the Callback Handler. Additionally, it includes a test private key that signed the payload and the corresponding public key that should be used for signature validation: > [!WARNING] > > The provided Private Key is for example purposes only and should not be used in your production system. > [!NOTE] > > The provided example may contain properties that are not sent by default. Customers should ensure they DO NOT expect to receive values that are not documented in the Approve Transactions and Approve Configuration Changes sections. ```text theme={"system"} eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eElkIjoiOWM3OTRjZWUtN2UyNy00NmM5LTllOWEtZWQ2ODI5NWZmMDZiIiwib3BlcmF0aW9uIjoiVFJBTlNGRVIiLCJzb3VyY2VUeXBlIjoiVkFVTFQiLCJzb3VyY2VJZCI6IjAiLCJkZXN0VHlwZSI6IlZBVUxUIiwiZGVzdElkIjoiMSIsImFzc2V0IjoiRVRIIiwiYW1vdW50IjowLjAxLCJhbW91bnRTdHIiOiIwLjAxMDAwMDAwMDAwMDAwMDAwMCIsInJlcXVlc3RlZEFtb3VudCI6MC4wMSwicmVxdWVzdGVkQW1vdW50U3RyIjoiMC4wMSIsImZlZSI6IjAuMDAwNTk3ODAzNzYyMjQxMDAwIiwiZGVzdEFkZHJlc3NUeXBlIjoiV0hJVEVMSVNURUQiLCJkZXN0QWRkcmVzcyI6IjB4NWRDNjlCMUZiYjEzQmFmZDA5YWY4OGE3ODJGMEYyODU3NzJBZDVmOCIsImRlc3RpbmF0aW9ucyI6W3siYW1vdW50TmF0aXZlIjowLjAxLCJhbW91bnROYXRpdmVTdHIiOiIwLjAxIiwiYW1vdW50VVNEIjoxOC43NDI5MjkzNywiZHN0QWRkcmVzc1R5cGUiOiJXSElURUxJU1RFRCIsImRzdElkIjoiMSIsImRzdFdhbGxldElkIjoiIiwiZHN0TmFtZSI6Ik5ldHdvcmsgRGVwb3NpdHMiLCJkc3RTdWJUeXBlIjoiIiwiZHN0VHlwZSI6IlZBVUxUIiwiZGlzcGxheURzdEFkZHJlc3MiOiIweDVkQzY5QjFGYmIxM0JhZmQwOWFmODhhNzgyRjBGMjg1NzcyQWQ1ZjgiLCJhY3Rpb24iOiJBTExPVyIsImFjdGlvbkluZm8iOnsiY2FwdHVyZWRSdWxlTnVtIjo1LCJydWxlc1NuYXBzaG90SWQiOjgxNjQsImJ5R2xvYmFsUG9saWN5IjpmYWxzZSwiYnlSdWxlIjp0cnVlLCJjYXB0dXJlZFJ1bGUiOiJ7XCJ0eXBlXCI6XCJUUkFOU0ZFUlwiLFwidHJhbnNhY3Rpb25UeXBlXCI6XCJUUkFOU0ZFUlwiLFwiYXNzZXRcIjpcIipcIixcImFtb3VudFwiOjAsXCJvcGVyYXRvcnNcIjp7XCJ3aWxkY2FyZFwiOlwiKlwifSxcImFwcGx5Rm9yQXBwcm92ZVwiOnRydWUsXCJhY3Rpb25cIjpcIkFMTE9XXCIsXCJzcmNcIjp7XCJpZHNcIjpbW1wiKlwiXV19LFwiZHN0XCI6e1wiaWRzXCI6W1tcIipcIl1dfSxcImRzdEFkZHJlc3NUeXBlXCI6XCIqXCIsXCJhbW91bnRDdXJyZW5jeVwiOlwiVVNEXCIsXCJhbW91bnRTY29wZVwiOlwiU0lOR0xFX1RYXCIsXCJwZXJpb2RTZWNcIjowfSJ9fV0sInJhd1R4IjpbeyJrZXlEZXJpdmF0aW9uUGF0aCI6IlsgNDQsIDYwLCAwLCAwLCAwIF0iLCJyYXdUeCI6IjAyZWYwMTA0ODQzYjlhY2EwMDg1MDZhMGMxOTg3ZDgyNTIwODk0NWRjNjliMWZiYjEzYmFmZDA5YWY4OGE3ODJmMGYyODU3NzJhZDVmODg3MjM4NmYyNmZjMTAwMDA4MGMwIiwicGF5bG9hZCI6Ijc3YjRlNzQwOTljZTkwYzA4NTAzYzBlMGJiNmU2NzJkYmUxYzVlM2UxMjdjZTMzM2JmMjJlYjU4MWNkM2Y2Y2UifV0sInBsYXllcnMiOlsiMjE5MjZlY2MtNGE4YS00NjE0LWJiYWMtN2M1OTFhYTdlZmRkIiwiMjc5MDA3MzctNDZmNi00MDk3LWExNjktZDBmZjQ1NjQ5ZWQ1IiwiZjg5Y2FjNTAtYzY1Ni00ZTc0LTg3OWYtMDQxYWZmOGQwMWI1Il0sInJlcXVlc3RJZCI6IjljNzk0Y2VlLTdlMjctNDZjOS05ZTlhLWVkNjgyOTVmZjA2YiJ9.aE1ZOSQreBU23q3e-Dx6Z76tFgURfDU8Szj1XESN1-LczwFpCXJeexOLJ4L5IoAHpp8FV8f1vg1yu_-iV1ZiPsCR8K9fcLQmTGF21Y43w18s1nvY8rqSKX64sHDWoBTUN1oJFhjzzi1ovpmcASBj3_yWJIv6-RfW20l30_f6ggMXZDE0QImCSYtRL2m5YIBr8wVe2rMleFLCQLUQTLVXyE-oxxbCfy-R_FqWv-dn7CaQSFkztm8fCn2_jnxlHs0phuOxSucwrOM7UkVr7qM8uTWhvdeTkYPjVBcj5lpgYwb8Fo2F0fnIvgcvRaz8r4LjVrj6hVwRHH809jt0ouzVeg ``` ```json theme={"system"} { "txId": "9c794cee-7e27-46c9-9e9a-ed68295ff06b", "operation": "TRANSFER", "sourceType": "VAULT", "sourceId": "0", "destType": "VAULT", "destId": "1", "asset": "ETH", "amount": 0.01, "amountStr": "0.010000000000000000", "requestedAmount": 0.01, "requestedAmountStr": "0.01", "fee": "0.000597803762241000", "destAddressType": "WHITELISTED", "destAddress": "0x5dC69B1Fbb13Bafd09af88a782F0F285772Ad5f8", "destinations": [ { "amountNative": 0.01, "amountNativeStr": "0.01", "amountUSD": 18.74292937, "dstAddressType": "WHITELISTED", "dstId": "1", "dstWalletId": "", "dstName": "Network Deposits", "dstSubType": "", "dstType": "VAULT", "displayDstAddress": "0x5dC69B1Fbb13Bafd09af88a782F0F285772Ad5f8", "action": "ALLOW", "actionInfo": { "capturedRuleNum": 5, "rulesSnapshotId": 8164, "byGlobalPolicy": false, "byRule": true, "capturedRule": "{\"type\":\"TRANSFER\",\"transactionType\":\"TRANSFER\",\"asset\":\"*\",\"amount\":0,\"operators\":{\"wildcard\":\"*\"},\"applyForApprove\":true,\"action\":\"ALLOW\",\"src\":{\"ids\":[[\"*\"]]},\"dst\":{\"ids\":[[\"*\"]]},\"dstAddressType\":\"*\",\"amountCurrency\":\"USD\",\"amountScope\":\"SINGLE_TX\",\"periodSec\":0}" } } ], "rawTx": [ { "keyDerivationPath": "[ 44, 60, 0, 0, 0 ]", "rawTx": "02ef0104843b9aca008506a0c1987d825208945dc69b1fbb13bafd09af88a782f0f285772ad5f8872386f26fc1000080c0", "payload": "77b4e74099ce90c08503c0e0bb6e672dbe1c5e3e127ce333bf22eb581cd3f6ce" } ], "players": [ "21926ecc-4a8a-4614-bbac-7c591aa7efdd", "27900737-46f6-4097-a169-d0ff45649ed5", "f89cac50-c656-4e74-879f-041aff8d01b5" ], "requestId": "9c794cee-7e27-46c9-9e9a-ed68295ff06b" } ``` ```yaml theme={"system"} -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJtgGHPlbV5KB1 yzPslxYnFiYeogUX0w6rd9Ee+zAljQ2msEP45NCaHwPX6fsukQnw+w7hlvr52aVR vKJKFmXnNMTSEd9v358053gJCzyVd9hKFnVO/04uwFTIwXX11VPd8XWZy6ue8meE qztGfJJvvDtHnbmJUFgf48DBv04hUzDm7yS2CJsO0UR91WQRwlTDkMUs4b2jJ4AN oLFaHlw+HAkKQH+O4g+WEfq+GQWnxq57L5U90TH4/+vELy54gOicojww6t/rTrjz WRjzJxfE6Ue3Tbpf7tfVMrlCsD+q3958q+m5a6O/KmwyfMo6P/yUt66W1KhK4Wmg v6YGY8J7AgMBAAECggEAG/rcvxNlJ+S/uLHta8n7dIQ3LnRKtjwnIp7gtGZN4++V 2gOaXNSfS7m1+/CE0Lf6w+GMy2XOPkk0DEtZIbjLUuU4Jhb87P+n4xv2mR6PaAEI piJJDHe6sx59GZ9IYHJCi3IP3ju+fIe4gf00oDmYddpg82AMGWGP5Ss1FPXB/eTV QT+5qwnZz2qqkzpk8QDcNjK/fsuuIaJg6RDC5NbXFuMr4NXKkXzDPkmzCgFowHAR J5Jqf4y2YArKV7kwEgW+naUAESPLWFUCaoLmCJxw7fbqHkUHYKHfWk9M9t9C0boL b0G9zbsAI/ip9YxuKzXi0E8lTkFH8q7Fssa5m/Gp/QKBgQD057qo2hq2zWXRXZ7W Q9nta3uNdsxhRPeC/z9ejUIy4kC7/ZbQ04+i+Tefwz4kjSVxSobb4Y7Bh8e4/tpw alyvTIyEys25G6vkFJh8MXojxQEgU1ImMnsFEqZVKsUilz/+aDG3POvihF4jwKbF GJCUhFG8mFzsFQLtYKmmp5NOXQKBgQDS2VWyS3d7arZyAdCjI3pw+SV/iF/bJ7wT NK2N6RHpfVwnnDk/AyFxQy2k53VwXASwYVsDkEZiPz1lqlV5xltcx3GDR5xhRdol M4i0H+Qc+bYXjy+O99LYgRODzTsq8PQmUADL9xAF+Df3CW4sTZ5Izi46Hq9uMFa6 bA0IH5nWtwKBgGkuuUFZ4w1N7APelKBrpcZNWlQoiKDiEPenDp1aR+s4txrGUCbC JjeVl6k7Ho5uPH2Kx57aIgjGeyXd9w0+8S2sz9EclPyCgPHFUrRMP6vrKY+rmWWk WqeUGfIMG3y+vxJRx8BuHtU7in8Kd9XAth/DMKOyQH54i7hNwq87241VAoGAQax6 Oc+xxppFe5s/HiFF2Pxxhpi2qq9ksGK/EC2ha6WlV50cY5kZCItRI0UI2ld/CmU4 kRKWKbHi8NCuUQDMokho/egHOHEmcmHr2Zb5WWEaK5poyNI+NTt3FZ2OKWDl2y0e Immw7vsSi3q/e0Mt4yV9VpMKN3sM+IIBSR92rl8CgYEAlfUVoSXRVOxh29dI65Qi 5IfJmg1mQnHWE2RfuZqtTcYisvbQejygsoyh1ZydEUSsX5rUbkc3/qqW5N4pEYVt ZHCn7MJHot44SA3XDn2dGRQAL5UQJJK5nHRVeBuoJtnNyzhxbZH3M56sT4o5i6UC TFalBSOqnaPpYIxV2fgCdyE= -----END PRIVATE KEY----- ``` ```yaml theme={"system"} -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybYBhz5W1eSgdcsz7JcW JxYmHqIFF9MOq3fRHvswJY0NprBD+OTQmh8D1+n7LpEJ8PsO4Zb6+dmlUbyiShZl 5zTE0hHfb9+fNOd4CQs8lXfYShZ1Tv9OLsBUyMF19dVT3fF1mcurnvJnhKs7RnyS b7w7R525iVBYH+PAwb9OIVMw5u8ktgibDtFEfdVkEcJUw5DFLOG9oyeADaCxWh5c PhwJCkB/juIPlhH6vhkFp8auey+VPdEx+P/rxC8ueIDonKI8MOrf606481kY8ycX xOlHt026X+7X1TK5QrA/qt/efKvpuWujvypsMnzKOj/8lLeultSoSuFpoL+mBmPC ewIDAQAB -----END PUBLIC KEY----- ``` *** ## Response payload example This example demonstrates the raw and decoded JWT response returned from the Callback Handler to the Co-signer. The response is signed using the Callback Handler's private key. The Co-signer then verifies the signature using the predefined public key configured during the Co-signer installation phase. For simplicity, the examples use the same private/public key pair as in the **Request Example** section: > [!NOTE] > > Please note that the iat value is not required in the actual response and will be ignored by the Co-Signer. It is included here because the JWT signature library automatically adds this field. ```text theme={"system"} eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY3Rpb24iOiJSRUpFQ1QiLCJyZXF1ZXN0SWQiOiI5Yzc5NGNlZS03ZTI3LTQ2YzktOWU5YS1lZDY4Mjk1ZmYwNmIiLCJyZWplY3Rpb25SZWFzb24iOiJMb2dpYyByZXR1cm5lZCBmYWxzZSIsImlhdCI6MTcyMDQ3MjU5NH0.CGJ7D0KHb2K0F0ibStAAy8MyvXIZR-sQsFRiHRebXPEVMOdjEooaDac9PZYTgmuMJANDtsDAy9bbIggaTraeGxLtHPS4TvQdOA1GX74gWlkBHktJxtGxngsSwbZ-clvCSrwQ0AQcwqCv1OozXRmt_SGberuJpVIUbNc1HWlKAbti4dIcN_irTp2Xwq9WmGRwF16rkK97JdtMbhPBF9t56kdYrHccKU9q69_4Q7zG8I9yXgYKTQ1XctJiGjg06dPR3fDEVeN_ibUXSU5h1sXnd3gvpbUNmEOYAREDKZAk_FMSdMmiC9Prp0t9g-xDtoSear6vN-ovxk_64AwMLxqsEg ``` ```json theme={"system"} { "action": "REJECT", "requestId": "9c794cee-7e27-46c9-9e9a-ed68295ff06b", "rejectionReason": "Logic returned false", "iat": 1720472594 } ``` *** ## API Co-Signer basic Callback Handler server example Here is a code example for a basic Callback Handler application. The application is designed to handle POST requests from the Co-signer at the endpoint /v2/tx\_sign\_request. * It validates that the received payload is signed with the correct private key. * If the verification is successful, it responds with a 200 status code, the `REJECT` action, and the `rejectionReason` in the response body. * If the verification fails, it responds with a 401 status code. ```javascript theme={"system"} const express = require("express"); const bodyParser = require("body-parser"); const fs = require("fs"); const jwt = require("jsonwebtoken"); const privateKey = fs.readFileSync("callback_private.pem"); const cosignerPubKey = fs.readFileSync("cosigner_public.pem"); const app = express(); app.use( express.urlencoded({ extended: true }) ); app.use(express.json()); app.use(function (req) { req.rawBody = ""; req.setEncoding("utf8"); req.on("data", function(chunk) { req.rawBody += chunk; }); req.on("end", function () { req.next(); }); }); app.post("/v2/tx_sign_request", async (req, res) => { let verified; try { const tx = jwt.decode(req.rawBody); const { requestId } = tx; verified = jwt.verify(req.rawBody, cosignerPubKey); if (verified) { let action = "REJECT"; let rejectionReason = "Logic returned false"; const signedRes = jwt.sign( { action, requestId, rejectionReason }, privateKey, { algorithm: "RS256" } ); res.send(signedRes); } } catch (e) { res.sendStatus(401); } }); app.listen(3000); ``` ```python theme={"system"} from pathlib import Path from wsgiref.simple_server import make_server import falcon import jwt callback_handler_prikey = None cosigner_pubkey = None # Load keys. f1 = Path("callback_private.pem") if f1.is_file(): callback_handler_prikey = f1.read_bytes() f2 = Path("cosigner_public.pem") if f2.is_file(): cosigner_pubkey = f2.read_bytes() class JWTTransferRequest(object): def on_post(self, req, resp): raw_req = req.bounded_stream.read() req = jwt.decode(raw_req, cosigner_pubkey, algorithms=["RS256"]) resp.body = jwt.encode({'action': 'APPROVE', 'requestId': req['requestId']}, callback_handler_prikey, algorithm="RS256") resp.status = falcon.HTTP_200 # Create falcon app app = falcon.App() app.add_route('/v2/tx_sign_request', JWTTransferRequest()) app.add_route('/v2/config_change_sign_request', JWTTransferRequest()) if __name__ == '__main__': with make_server('', 80, app) as httpd: print('Serving on port 80...') # Serve until process is killed httpd.serve_forever() JWTTransferRequest() ``` # Securing communication Source: https://developers.fireblocks.com/cosigners/callback-handler/cosigner-callbackhandler-secure-communication-authentication ## Authenticating the Callback Handler Response Object When setting up the Callback Handler server for an API user paired with the Co-signer, you can choose between five options to secure the communication between them. This selection determines the payload structure of the POST request sent by the Co-signer to the Callback Handler server. It also defines how the request is authenticated by the Callback Handler. More details are provided below. > [!WARNING] > > Configuring the Callback Handler from the Console supports only Option 1 and Option 2. If you provide a public key in the Console, the Co-signer uses Option 1. If you provide a certificate, the Co-signer uses Option 2 and pins that certificate. The Console does not configure the certificate as a Root-CA certificate, and it does not combine a public key and a certificate. > > Options 3, 4, and 5 are currently supported only through local configuration with the Co-signer scripts on the Co-signer's host machine. See [Co-signer command-line operations](/cosigners/operations/api-cosigner-operate#co-signer-command-line-operations). *** ## Option 1: Public key authentication In this option, the Co-signer and the Callback Handler exchange JSON Web Token (JWT) encoded messages, each signed with their respective private keys. The Co-signer's POST request to the Callback Handler server and the Callback Handler's response to the Co-signer are authenticated using their corresponding public keys. ### JWT-encoded signed request * The Co-signer, acting as a client of the Callback Handler server, sends a POST request and uses its private key to sign the JWT-encoded message. * The Callback Handler uses the Co-signer's public key to authenticate the message and ensure it originated from the Co-signer. * You can retrieve the Co-signer's public key directly from the Co-signer ([see the guides](/cosigners/operations/api-cosigner-maintenance)). > [!NOTE] > > The same Co-signer private key is used to sign request messages sent to the Callback Handler server for all API users paired with this Co-signer. ### JWT-encoded signed response * The Callback Handler responds with a JWT-encoded message signed with its private key. * The Co-signer uses the Callback Handler's public key to authenticate the message and ensure it originated from it. * You provide the API user's Callback Handler public key to the Co-signer during installation or while configuring the Callback Handler for that API user. This public key corresponds to the private key used by the Callback Handler to sign JWT-encoded response messages. Follow these steps to generate a private-public key pair for signing JWT-encoded responses. This allows the Co-signer to authenticate your Callback Handler's responses using the corresponding public key: 1. Generate a private key. You can use an RSA 2048-bit key, or, on Co-signer version 2026.08.19 or later, an ECDSA P-256 key: ```bash RSA 2048 theme={"system"} openssl genrsa -out callback_private.pem 2048 ``` ```bash ECDSA P-256 theme={"system"} openssl ecparam -name prime256v1 -genkey -noout -out callback_private.pem ``` 2. Export the public key from the private key you generated: ```bash RSA 2048 theme={"system"} openssl rsa -in callback_private.pem -outform PEM -pubout -out callback_public.pem ``` ```bash ECDSA P-256 theme={"system"} openssl ec -in callback_private.pem -outform PEM -pubout -out callback_public.pem ``` > [!NOTE] > > Sign the JWT-encoded responses with the algorithm that matches your key: `RS256` for an RSA key, or `ES256` for an ECDSA P-256 key. The Co-signer rejects responses signed with any other algorithm. 3. Use this public key to configure the Co-signer's Callback Handler for the specified API user. 4. Set up the Callback Handler as an HTTPS server using a certificate signed by a trusted Certificate Authority (CA). Configure the Callback Handler endpoints to **include** a `/v2` prefix in their URLs: `https://your_callback_base_url/v2/tx_sign_request` or `https://your_callback_base_url/v2/config_change_sign_request` ### Communication channel security TLS communication between the Co-signer and the Callback Handler server requires the Callback Handler to operate as an HTTPS server with a certificate signed by a trusted Certificate Authority (CA). *** ## Option 2: Self-Signed Certificate-based communication (Certificate pinning) In this option, a self-signed certificate or a certificate signed by a trusted Certificate Authority (CA) is used to establish certificate-pinning-based secure communication between the Co-signer and the Callback Handler. TLS certificate authentication occurs during SSL negotiation and is based on the certificate you provide to the Co-signer. The certificate is provided to the Co-signer during installation or while configuring the Callback Handler for a specific API user. In this setup, both the Co-signer's POST request to the Callback Handler server and the server's response are in JSON format, instead of using signed JWTs. Create a certificate and sign it yourself or have it signed by a trusted CA; set up an HTTPS server and configure your Callback Handler endpoints to respond to requests that **do not include** a `/v2` prefix: `https://your_callback_base_url/tx_sign_request` or `https://your_callback_base_url/config_change_sign_request` *** ## Option 3: Root-CA Certificate-based communication > [!NOTE] > > This feature is supported by SGX Co-signer version 2025.12.11 or later, and AWS Nitro Co-signer version 2026.08.19 or later. In this option, a Root-CA certificate is used to establish certificate-based secure communication between the Co-signer and the Callback Handler. TLS certificate authentication occurs during SSL negotiation and is based on the Root-CA certificate you provide to the Co-signer. The Root-CA certificate is provided to the Co-signer during installation or while configuring the Callback Handler for a specific API user. A certificate that was signed by the Root-CA needs to be provided to the Co-signer by the Callback Handler during the TLS certificate authentication. In this setup, both the Co-signer's POST request to the Callback Handler server and the server's response are in JSON format, instead of using signed JWTs. Create a certificate and have it signed by the Root-CA; set up an HTTPS server and configure your Callback Handler endpoints to respond to requests that **do not include** a `/v2` prefix: `https://your_callback_base_url/tx_sign_request` or `https://your_callback_base_url/config_change_sign_request` *** ## Option 4: Hybrid - Public key authentication with certificate pinning > [!NOTE] > > This feature is supported by SGX Co-signer version 2025.12.11 or later, and AWS Nitro Co-signer version 2026.08.19 or later. This hybrid option combines the message-level security of Option 1 (public key authentication) with the transport-level security of Option 2 (certificate pinning). In this configuration: * **TLS Layer**: The secure TLS connection is established using certificate pinning with a self-signed certificate or a certificate signed by a trusted Certificate Authority (CA), as described in Option 2. * **Message Layer**: Communication messages are exchanged as JWT-encoded messages signed with private keys, exactly as described in Option 1. This approach provides dual-layer security: 1. The TLS handshake authenticates the Callback Handler server using the pinned certificate 2. The JWT signatures authenticate individual messages using public key cryptography ### Configuration steps: 1. Create a certificate and sign it yourself or have it signed by a trusted CA 2. Provide the certificate to the Co-signer during installation or while configuring the Callback Handler for the specific API user 3. Generate and configure JWT signing keys as described in Option 1: 1. Generate a private-public key pair for the Callback Handler 2. Provide the public key to the Co-signer for JWT verification 4. Configure your Callback Handler endpoints to include a /v2 prefix (as in Option 1): > `https://your_callback_base_url/v2/tx_sign_request` > or > `https://your_callback_base_url/v2/config_change_sign_request` 5. Implement your Callback Handler to: 1. Accept JWT-encoded requests from the Co-signer 2. Respond with JWT-encoded messages signed with your private key *** ## Option 5: Hybrid - Public key authentication with Root-CA certificate > [!NOTE] > > This feature is supported by SGX Co-signer version 2025.12.11 or later, and AWS Nitro Co-signer version 2026.08.19 or later. This hybrid option combines the message-level security of Option 1 (public key authentication) with the transport-level security of Option 3 (Root-CA certificate validation). In this configuration: * **TLS Layer**: The secure TLS connection is established using Root-CA certificate validation, as described in Option 3. * **Message Layer**: Communication messages are exchanged as JWT-encoded messages signed with private keys, exactly as described in Option 1. This approach provides dual-layer security: 1. The TLS handshake authenticates the Callback Handler server using Root-CA certificate validation 2. The JWT signatures authenticate individual messages using public key cryptography ### Configuration steps: 1. Create a certificate and have it signed by your Root-CA 2. Provide the Root-CA certificate to the Co-signer during installation or while configuring the Callback Handler for the specific API user 3. Generate and configure JWT signing keys as described in Option 1: 1. Generate a private-public key pair for the Callback Handler 2. Provide the public key to the Co-signer for JWT verification 4. Configure your Callback Handler endpoints to include a `/v2` prefix (as in Option 1): > `https://your_callback_base_url/v2/tx_sign_request` > or > `https://your_callback_base_url/v2/config_change_sign_request` 5. Implement your Callback Handler to: 1. Accept JWT-encoded requests from the Co-signer 2. Respond with JWT-encoded messages signed with your private key > [!NOTE] > > When adding the Callback Handler URL to the Co-signer, you should pass only the base URL + custom relative path. > The `/v2/tx_sign_request` or `/v2/config_change_sign_request` relative paths will be automatically added upon each request made by the Co-signer. > > For example if you exposed the Callback Handler URL in the following path: > `https://subdomain.example.com/fireblocks/callback_handler/v2/tx_sign_request` > > You should configure the following URL in the Co-signer: > `https://subdomain.example.com/fireblocks/callback_handler` # Setup API Co-signer Callback Handler Source: https://developers.fireblocks.com/cosigners/callback-handler/create-api-co-signer-callback-handler ## Overview The API Co-signer Callback Handler places your custom authorization logic at the signing boundary, separate from your application. When you configure a Callback Handler for an API user paired with a Co-signer, the Co-signer sends every signing or approval request for that user to your handler and requires its response before proceeding. This gives your organization an independent control point that continues to protect signing if your application or its API credentials are compromised. > [!NOTE] > > **The Callback Handler is a service hosted and managed by your organization.** You build, host, and operate it in your own environment. The Co-signer acts as its client and waits for its response before proceeding with a signing or approval request. Your Callback Handler can apply business, security, or compliance logic before transactions are automatically signed or approved. If no Callback Handler is configured for an API user, the Co-signer automatically signs or approves all requests it receives for that user. ## Security enforced at signing or approval time Checks implemented inside your application run before a transaction request reaches Fireblocks. Your application alone enforces those checks, so a caller with valid API credentials can submit transactions without passing through them. The Callback Handler moves your custom validation into an independent authorization layer that the Co-signer invokes at signing or approval time. Your handler determines what should be signed or approved, and the Co-signer enforces through Multi-Party Computation (MPC) that only an approved request produces a valid signature. A compromise of your application or its API credentials therefore cannot bypass the Callback Handler. > [!TIP] > > **You define the validation logic, and the Co-signer enforces the decision.** Authorization and signing remain in separate trust domains, so your application cannot produce a valid signature by itself. The trust boundaries look like this: ```mermaid theme={"system"} flowchart LR subgraph Customer["Your environment"] App["Your application"] CS["API Co-signer"] CH["Callback Handler"] end subgraph FB["Fireblocks platform"] API["Fireblocks API and Policy engine"] CC["Cloud Co-signers
(MPC key shares)"] end App -->|"API request"| API CS -->|"Polls for signing or approval requests"| API CS <-->|"Authenticated callback"| CH CS <-->|"MPC signing"| CC ``` Your Callback Handler can approve a request, but it cannot create a signature. The Co-signer participates in MPC signing only after the handler approves the request. See the [API Co-signers Architecture Overview](/cosigners/overview/cosigner-architecture-overview) for how key shares are generated and protected. The Callback Handler complements the Fireblocks Policy engine rather than replacing it: | Layer | Where it runs | What it enforces | | - | - | - | | Checks in your application | Your application, before the request reaches Fireblocks | Your application logic for requests that pass through the application | | Fireblocks Policy | The Fireblocks platform, on every transaction | Governance rules for who can move funds and which amounts, sources, and destinations are allowed | | Callback Handler | Your server, called by the Co-signer at signing or approval time | Your custom business and security logic for every signing or approval request associated with the paired API user | ### Built-in protections The integration also protects communication and failure behavior by default: * **Authenticated communication:** Public key authentication signs requests and responses as JSON Web Tokens (JWTs). Certificate-based and hybrid authentication options are also available. See [Securing communication](/cosigners/callback-handler/cosigner-callbackhandler-secure-communication-authentication). * **Fail-closed behavior:** The request fails if your Callback Handler rejects it or does not respond in time, so the Co-signer does not sign without approval. * **Enclave-protected enforcement:** The Co-signer that enforces your Callback Handler's decision runs inside a hardened enclave. See the [API Co-signers Architecture Overview](/cosigners/overview/cosigner-architecture-overview). ## How it works When a Policy rule designates an API user paired with the Co-signer as the signer or approver, the Co-signer calls your Callback Handler before it acts: ```mermaid theme={"system"} sequenceDiagram participant App as Your application participant FB as Fireblocks participant CS as API Co-signer
(your environment) participant CH as Callback Handler
(your server) App->>FB: Create transaction (REST API) FB->>FB: Evaluate Policy rules FB->>CS: Signing or approval request CS->>CH: Authenticated request CH->>CH: Authenticate the request and run your custom logic CH-->>CS: Authenticated response (APPROVE / REJECT / RETRY) alt APPROVE CS->>FB: Transaction is signed (MPC) else RETRY CS->>CH: Resends the request according to the retry policy else REJECT or no response CS->>FB: Request fails end ``` 1. Your application creates a transaction through the Fireblocks API. 2. Fireblocks evaluates your [Policy](https://support.fireblocks.io/hc/en-us/articles/29184395887772-About-Policies) rules. If the designated signer is an API user paired with the Co-signer, the request is routed to the Co-signer. 3. The Co-signer sends a request to your Callback Handler with the request details. 4. Your Callback Handler runs your custom logic and responds with a [CallbackResponse](/cosigners/callback-handler/response-object) action. 5. The Co-signer signs or approves the request only if your Callback Handler responds with `APPROVE`. A `RETRY` response tells the Co-signer to resend the request according to its retry policy. A `REJECT` response or a missing response causes the request to fail. See the [Callback Handler response object](/cosigners/callback-handler/response-object) for response behavior and timing. The flow is fail-closed by default because the Co-signer requires approval from your Callback Handler before it signs. > [!NOTE] > > **Cold workspaces** > > In cold workspaces, transaction signing is performed offline using the cold mobile app, so the API Co-signer never signs. The Co-signer still participates in the approval stage, and a Callback Handler can be configured for API users paired with it in that role: > > * **Non-Signing Admin** — transaction approvals and configuration change approvals. > * **Approver** — transaction approvals. > > Pair a Non-Signing Admin or Approver API user with the Co-signer, then configure a Callback Handler for that user to apply your approval logic to incoming requests. ## Securing communication between the Co-signer and the Callback Handler When configuring the Callback Handler for an API user paired with the Co-signer, you select how the communication between the Co-signer (which acts as a client) and the Callback Handler server is authenticated. Fireblocks supports public key, certificate-based, and hybrid authentication modes. For the full list of authentication options and their configuration steps, see [Securing communication](/cosigners/callback-handler/cosigner-callbackhandler-secure-communication-authentication). ## Best practices * **Isolate the Callback Handler.** Run it on a server separate from the Co-signer instance, and restrict both the Co-signer and the Callback Handler from open connections to external locations unless necessary. This minimizes the attack surface around your signing logic. * **Respond quickly and reliably.** MPC signing does not begin until your Callback Handler approves the request. If your logic needs more time, respond with `RETRY` as described in the [Callback Handler response object](/cosigners/callback-handler/response-object). * **Handle retries idempotently.** Retried requests are sent with identical parameters, so make sure your Callback Handler deals with the asynchronous nature of the calls and does not treat a retry as a new request. * **Use HTTPS in production.** Serve the Callback Handler with a valid TLS certificate from a trusted Certificate Authority (CA). * **Start from the boilerplate.** The [Plugin-based Callback Handler](/cosigners/callback-handler/plugin-based-callback-handler) provides a pre-built server application so you can focus on your custom logic instead of server development. * **Harden the server itself.** Operational hardening of the Callback Handler machine, such as access restrictions and monitoring, is covered in the [API Co-signer Security Checklist](/cosigners/overview/co-signer-security-checklist-defense-monitoring). # Plugin-based Callback Handler Source: https://developers.fireblocks.com/cosigners/callback-handler/plugin-based-callback-handler The Fireblocks Plugin-based Callback Handler is a boilerplate application designed to simplify the installation and setup process for Fireblocks users. It integrates seamlessly with plugins, enabling you to easily develop custom functionalities without investing time and resources in server application development. The application comes with a collection of pre-configured plugins, which are described in detail below for your reference. Check out the Plugin-Based Callback Handler boilerplate in [our official GitHub account](https://github.com/fireblocks/plugin-based-callback-handler). # Callback Handler response object Source: https://developers.fireblocks.com/cosigners/callback-handler/response-object > [!NOTE] > > **New Callback Handler parameters** > > Starting with SGX API Co-signer image version 3.5.0, as well as all versions of AWS Nitro and GCP Confidential Space Co-signer, the following enhancements have been introduced: > > * The Callback Handler's payload includes the **signerId**, which represents the **API user ID** (API key) of the API user handling the request. This is particularly useful in high availability configurations with multiple API Co-signers, as it allows the Callback Handler to identify the specific Co-signer that initiated the request. Below is the response expected from the Callback Handler for any request. If it does not respond within 30 seconds, Fireblocks fails the request. | **Parameter** | **Type** | **Description** | | - | - | - | | action | string | `APPROVE`: Approves the request. For some configuration requests an approval quorum may be required. `REJECT`: Denies the request. Even if a quorum of approvals is expected, one rejection denies the request. On a *transaction signing* request, `REJECT` fails the transaction permanently: it is *not* deferred to a Console user (human signer), and no other signer is asked. Do not use `REJECT` to route a transaction to a manual signer. `RETRY`: Retry the request until it times out, reaches the maximum number of retries, or the Callback Handler returns `APPROVE` or `REJECT`. The default timeout setting is 20 retries every 3 minutes, for a total of 60 minutes. If necessary, Fireblocks Support can adjust the retry interval to a minimum of 30 seconds or a maximum of 1 hour, and the total retry duration up to a maximum of 48 hours. Additionally, retries are performed using identical parameters, so to prevent duplication, make sure your Callback Handler is properly configured to deal with the asynchronous nature of the calls. `IGNORE`: This action is only supported for transaction and configuration approvals, not transaction signing. Dismisses the request without denying it, allowing the following approval requests to be processed. If a quorum is required to approve a request, the other approvers may approve this request independently. | | requestId | string | The unique identifier of the call, as received in the approval request. | | rejectionReason | string | (Optional) Free text of the reason for rejection. Fireblocks doesn't store this value, so log it in your Callback Handler if you need a record. | ## Where to see the Callback Handler's response * **Transaction sub-status:** If the Callback Handler returns `REJECT`, the transaction is `CANCELLED` with sub-status `REJECTED_BY_USER`. If the Co-signer doesn't get a valid response (invalid JSON, an HTTP error, or no response within 30 seconds), the transaction is `FAILED` with sub-status `AUTHORIZATION_FAILED`. See [Sub-statuses](/reference/sub-statuses). * **Your Callback Handler's logs:** Fireblocks doesn't store the `rejectionReason`. To keep a record of each request, the `action` you returned and the reason, log them in your Callback Handler. > [!NOTE] > > **Routing a transaction to a Console user instead of the Co-signer** > > The Callback Handler cannot hand a transaction signing request over to a Console user. If the Policy rule designates an API user as the signer, or a signer group that includes an API user, the Co-signer signs the transaction without routing it to a Console user. > > **Recommended approach:** to have a Console user sign, configure the Policy rule so that the designated signer or signer group does not include the API user. This is a Policy configuration, not a Callback Handler response. > > **Approvals are different from signing.** `IGNORE` is valid for transaction and configuration *approval* requests only, never for signing requests. Returning `IGNORE` on an approval request dismisses it without denying it, so a Console user (human approver) can approve it independently. Once approved, the Co-signer still performs the signing if it is the designated signer. # Validate ETH raw transactions Source: https://developers.fireblocks.com/cosigners/callback-handler/validate-eth-raw-transactions Fireblocks allows you to verify ETH transactions before they are signed by the API Co-signer. You can configure your workspace to receive the raw data of the ETH transaction as part of the payload sent to the Callback Handler. In this article, we are going to cover how to verify Ethereum raw transactions. ## ETH - Callback Handler payload structure First, let’s take a look at the payload that is sent from the Co-signer to the Callback Handler (a detailed spec can be found [here](/cosigners/callback-handler/approve-transactions)): ```json theme={"system"} { "txId": "9c794cee-7e27-46c9-9e9a-ed68295ff06b", "operation": "TRANSFER", "sourceType": "VAULT", "sourceId": "0", "destType": "VAULT", "destId": "1", "asset": "ETH", "amount": 0.01, "amountStr": "0.010000000000000000", "requestedAmount": 0.01, "requestedAmountStr": "0.01", "fee": "0.000597803762241000", "destAddressType": "WHITELISTED", "destAddress": "0x5dC69B1Fbb13Bafd09af88a782F0F285772Ad5f8", "destinations": [ { "amountNative": 0.01, "amountNativeStr": "0.01", "amountUSD": 18.74292937, "dstAddressType": "WHITELISTED", "dstId": "1", "dstWalletId": "", "dstName": "Network Deposits", "dstSubType": "", "dstType": "VAULT", "displayDstAddress": "0x5dC69B1Fbb13Bafd09af88a782F0F285772Ad5f8", "action": "ALLOW", "actionInfo": { "capturedRuleNum": 5, "rulesSnapshotId": 8164, "byGlobalPolicy": false, "byRule": true, "capturedRule": "{\"type\":\"TRANSFER\",\"transactionType\":\"TRANSFER\",\"asset\":\"*\",\"amount\":0,\"operators\":{\"wildcard\":\"*\"},\"applyForApprove\":true,\"action\":\"ALLOW\",\"src\":{\"ids\":[[\"*\"]]},\"dst\":{\"ids\":[[\"*\"]]},\"dstAddressType\":\"*\",\"amountCurrency\":\"USD\",\"amountScope\":\"SINGLE_TX\",\"periodSec\":0}" } } ], "rawTx": [ { "keyDerivationPath": "[ 44, 60, 0, 0, 0 ]", "rawTx": "02ef0104843b9aca008506a0c1987d825208945dc69b1fbb13bafd09af88a782f0f285772ad5f8872386f26fc1000080c0", "payload": "77b4e74099ce90c08503c0e0bb6e672dbe1c5e3e127ce333bf22eb581cd3f6ce" } ], "players": [ "21926ecc-4a8a-4614-bbac-7c591aa7efdd", "27900737-46f6-4097-a169-d0ff45649ed5", "f89cac50-c656-4e74-879f-041aff8d01b5" ], "requestId": "9c794cee-7e27-46c9-9e9a-ed68295ff06b" } ``` The payload above contains a lot of information but we will be focusing only on some parts of it: 1. Amount (`destinations[0].amountNative`) 2. Destination Address (`destinations[0].displayDstAddress`) 3. Raw Transaction array (`rawTx`) RLP encoded payload (`rawTx.rawTx`) The hash of the raw transaction (`rawTx.payload`) Note that the RLP encoded payload (`rawTx.rawTx`) is the actual payload that you are signing on, or to be precise, the signature is done over the keccak256 hash of the RLP encoded payload, which is exactly the hash provided in the `rawTx.payload` property. *** ## Creating the Callback Handler application Before diving into the verification of the ETH transaction process, let’s start with spinning up our Callback Handler server. In this example I am going to use the Express.js framework: Install Express: `npm i express` We will also need some additional packages to be installed: `npm i jsonwebtoken fs body-parser @ethereumjs/tx` Initiating the app: ```javascript theme={"system"} const port = 8080; const app = express(); ``` Let’s set our middleware (we are using body-parser here to access the raw body sent over to our POST endpoint that we will add shortly): ```javascript theme={"system"} const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()) app.use( (req) => { req.rawBody = ""; req.setEncoding("utf8"); req.on("data", (chunk) => { req.rawBody += chunk; }); req.on("end", () => { req.next(); }); } ); ``` Defining our route for the `/v2/tx_sign_request` endpoint: ```javascript theme={"system"} app.post("/v2/tx_sign_request", (req, res) => { console.log("The raw body of the HTTP request:", req.rawBody) } ``` To understand how the `rawBody` of the HTTP request is obtained, let's break down the process: **Mutual Authentication via Signed JWTs:** * The Fireblocks API Co-signer does not transmit the payload in plain text. Instead, it employs an authentication mechanism using signed JSON Web Tokens (JWTs) for secure communication. * Each request sent by the Co-signer to the Callback Handler includes a signed JWT in the request body. This JWT is signed using the Co-signer application's private key. * Similarly, any response from the Callback Handler to the Co-signer must also be signed using the Callback Handler's private key. **Key Exchange Requirements:** * To enable this mutual authentication, both the Callback Handler and the Co-signer must exchange their respective public keys. * The Callback Handler should have access to: * Its own private key for signing responses. * The Co-signer's public key to verify incoming requests. * The Co-signer requires: * Its private key for signing requests. * The Callback Handler's public key to validate responses. **Pre-requisite:** It is assumed that you have completed the authentication setup and have access to the required keys: * Callback Handler's private and public keys. * Co-signer's public key. By ensuring these keys are in place, the Callback Handler can securely verify and respond to requests while maintaining the integrity of communication with the Co-signer. Loading the Callback Handler’s private key and the Co-signer's public key: ```javascript theme={"system"} const fs = require("fs"); // Read the callback handler private key const privateKey = fs.readFileSync("callback_private.pem"); // Read the cosigner public key (you can get it by running: ./cosigner print-public-key on the cosigner machine) const cosignerPubKey = fs.readFileSync("cosigner_public.pem"); ``` In our example, the raw body of the HTTP request will actually look like this: ```text theme={"system"} eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0eElkIjoiNDhmNjg3MWMtMDQ2Ny00YjU2LThjNDQtNDI2ZjNkNjQ4N2Y1Iiwib3BlcmF0aW9uIjoiVFJBTlNGRVIiLCJzb3VyY2VUeXBlIjoiVkFVTFQiLCJzb3VyY2VJZCI6IjAiLCJkZXN0VHlwZSI6IlZBVUxUIiwiZGVzdElkIjoiMSIsImFzc2V0IjoiRVRIIiwiYW1vdW50IjowLjAxMDAwMDAwLCJhbW91bnRTdHIiOiIwLjAxMDAwMDAwMDAwMDAwMDAwMCIsInJlcXVlc3RlZEFtb3VudCI6MC4wMTAwMDAwMCwicmVxdWVzdGVkQW1vdW50U3RyIjoiMC4wMSIsImZlZSI6IjAuMDAwNTg0MjYxNjA3NjU3MDAwIiwiZGVzdEFkZHJlc3NUeXBlIjoiV0hJVEVMSVNURUQiLCJkZXN0QWRkcmVzcyI6IjB4NWRDNjlCMUZiYjEzQmFmZDA5YWY4OGE3ODJGMEYyODU3NzJBZDVmOCIsImRlc3RpbmF0aW9ucyI6W3siYW1vdW50TmF0aXZlIjowLjAxMDAwMDAwLCJhbW91bnROYXRpdmVTdHIiOiIwLjAxIiwiYW1vdW50VVNEIjoxOC43NDUwMTkzNiwiZHN0QWRkcmVzc1R5cGUiOiJXSElURUxJU1RFRCIsImRzdElkIjoiMSIsImRzdFdhbGxldElkIjoiIiwiZHN0TmFtZSI6Ik5ldHdvcmsgRGVwb3NpdHMiLCJkc3RTdWJUeXBlIjoiIiwiZHN0VHlwZSI6IlZBVUxUIiwiZGlzcGxheURzdEFkZHJlc3MiOiIweDVkQzY5QjFGYmIxM0JhZmQwOWFmODhhNzgyRjBGMjg1NzcyQWQ1ZjgiLCJhY3Rpb24iOiJBTExPVyIsImFjdGlvbkluZm8iOnsiY2FwdHVyZWRSdWxlTnVtIjo1LCJydWxlc1NuYXBzaG90SWQiOjgxNjQsImJ5R2xvYmFsUG9saWN5IjpmYWxzZSwiYnlSdWxlIjp0cnVlLCJjYXB0dXJlZFJ1bGUiOiJ7XCJ0eXBlXCI6XCJUUkFOU0ZFUlwiLFwidHJhbnNhY3Rpb25UeXBlXCI6XCJUUkFOU0ZFUlwiLFwiYXNzZXRcIjpcIipcIixcImFtb3VudFwiOjAsXCJvcGVyYXRvcnNcIjp7XCJ3aWxkY2FyZFwiOlwiKlwifSxcImFwcGx5Rm9yQXBwcm92ZVwiOnRydWUsXCJhY3Rpb25cIjpcIkFMTE9XXCIsXCJzcmNcIjp7XCJpZHNcIjpbW1wiKlwiXV19LFwiZHN0XCI6e1wiaWRzXCI6W1tcIipcIl1dfSxcImRzdEFkZHJlc3NUeXBlXCI6XCIqXCIsXCJhbW91bnRDdXJyZW5jeVwiOlwiVVNEXCIsXCJhbW91bnRTY29wZVwiOlwiU0lOR0xFX1RYXCIsXCJwZXJpb2RTZWNcIjowfSJ9fV0sInJhd1R4IjpbeyJrZXlEZXJpdmF0aW9uUGF0aCI6IlsgNDQsIDYwLCAwLCAwLCAwIF0iLCJyYXdUeCI6IjAyZWYwMTA0ODQzYjlhY2EwMDg1MDY3YTUxYmU4NTgyNTIwODk0NWRjNjliMWZiYjEzYmFmZDA5YWY4OGE3ODJmMGYyODU3NzJhZDVmODg3MjM4NmYyNmZjMTAwMDA4MGMwIiwicGF5bG9hZCI6IjZjYTE4YzQ3Y2NkOGRkNDdiYjBiMGIwYzczY2M1MjM3YmJmZjlkMGVhYWU0NjljYTY1YTY3MjMyOTY0M2JmZGEifV0sInBsYXllcnMiOlsiMjE5MjZlY2MtNGE4YS00NjE0LWJiYWMtN2M1OTFhYTdlZmRkIiwiMjc5MDA3MzctNDZmNi00MDk3LWExNjktZDBmZjQ1NjQ5ZWQ1IiwiZjg5Y2FjNTAtYzY1Ni00ZTc0LTg3OWYtMDQxYWZmOGQwMWI1Il0sInJlcXVlc3RJZCI6IjQ4ZjY4NzFjLTA0NjctNGI1Ni04YzQ0LTQyNmYzZDY0ODdmNSJ9.00000000000000000000000000000000000000000000000 ``` > [!NOTE] > > The signature part of the JWT is obfuscated as a security best practice. If you take this JWT and parse it in jwt.io - you’ll get the same clear text JSON payload as at the beginning of this guide. *** ## JWT verification So what should we do with this JWT? We need to verify its signature by using the Co-Signer public key and we need to decode it to JSON: ```javascript theme={"system"} const jwt = require("jsonwebtoken"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()) app.use( (req) => { req.rawBody = ""; req.setEncoding("utf8"); req.on("data", (chunk) => { req.rawBody += chunk; }); req.on("end", () => { req.next(); }); } ); app.post("/v2/tx_sign_request", (req, res) => { try { // jwt.verify returns the decoded payload on successful verification const tx = jwt.verify(req.rawBody, cosignerPubKey); } catch(e) { console.error(e) res.sendStatus(401).send(); } } ``` *** ## Building the ETH transaction object So now we have set the authentication, and if the token is verified, we can actually move on to the transaction validation part.Let's define a new function, validateETHTransaction. It will get the plain text payload and will build an ETH transaction object from the RLP encoded hex: ```javascript theme={"system"} const { FeeMarketEIP1559Transaction } = require('@ethereumjs/tx'); const validateETHTransaction = (payload) => { try { // Decode RLP const unsignedTx = FeeMarketEIP1559Transaction.fromSerializedTx( Buffer.from( payload.rawTx[0].rawTx, "hex" )) } catch(e) { console.log("Error:", e) } } ``` Basically, we are taking the RLP-encoded hex and building the transaction object using the `FeeMarketEIP1559Transaction` class from @ethereumjs/tx. If we print the unsignedTx object, we’ll get: ```json theme={"system"} { "chainId": "0x1", "nonce": "0x4", "maxPriorityFeePerGas": "0x4d4ea640", "maxFeePerGas": "0x7c4b19ac5", "gasLimit": "0x5208", "to": "0x5dc69b1fbb13bafd09af88a782f0f285772ad5f8", "value": "0x2386f26fc10000", "data": "0x", "accessList": [] } ``` *** ## Verifying the transaction parameters Now we can actually check whether the destination and the amount in the decoded transaction match the JSON values of the payload: ```javascript theme={"system"} const { FeeMarketEIP1559Transaction } = require('@ethereumjs/tx'); const validateETHTransaction = (payload) => { try { // Decode RLP const unsignedTx = FeeMarketEIP1559Transaction.fromSerializedTx( Buffer.from( payload.rawTx[0].rawTx, "hex" )) return ( parseFloat(unsignedTx.value, 16) / 1e18 === payload.destinations[0].amountNative && unsignedTx.to.toString("hex") === payload.destinations[0].displayDstAddress.toLowerCase() ) } catch (e) { console.log("Error:", e) } } ``` > [!NOTE] > > The address in the payload is in checksum format, so we need to lowercase it. We can actually add one more check - verify that the hash of the unsignedTx matches the provided hash in the payload: ```javascript theme={"system"} const { FeeMarketEIP1559Transaction } = require('@ethereumjs/tx'); const validateETHTransaction = (payload) => { try { // Decode RLP const unsignedTx = FeeMarketEIP1559Transaction.fromSerializedTx( Buffer.from( payload.rawTx[0].rawTx, "hex" )) return ( parseFloat(unsignedTx.value, 16) / 1e18 === payload.destinations[0].amountNative && unsignedTx.to.toString("hex") === payload.destinations[0].displayDstAddress.toLowerCase() && unsignedTx.getMessageToSign(true).toString("hex") == providedHash ) } catch (e) { console.log("Error:", e) } } ``` The code above extracts the hash from the payload provided by the Co-signer and compares it with the result of `unsignedTx.getMessageToSign(true)`. This method returns the hash of the unsigned transaction that is expected to be signed. *** ## Callback Handler response The response from the Callback Handler should be in the following format (signed with `RS256` algorithm by using the Callback Handler's private key): ```json theme={"system"} { action: 'APPROVE' OR 'REJECT' OR 'IGNORE', requestId: 'The unique identifier of the call that was received in the approval request', rejectionReason: (Optional) 'Free text of the rejection reason for logging purposes' } ``` Let’s just add one more function that will generate the signed response that should be sent from the Callback Handler: ```javascript theme={"system"} const generateSignedResponse = (action) => { const signedRes = jwt.sign( action, privateKey, { algorithm: "RS256" } ); return signedRes } ``` *** ## Piecing all the parts together ```javascript theme={"system"} const fs = require("fs"); const express = require("express"); const jwt = require("jsonwebtoken"); const bodyParser = require("body-parser"); const { FeeMarketEIP1559Transaction } = require('@ethereumjs/tx'); // Read the callback handler private key const privateKey = fs.readFileSync("private.pem"); // Read the cosigner public key (you can get it by running: ./cosigner print-public-key on the cosigner machine) const cosignerPubKey = fs.readFileSync("cosigner_public.pem"); // Start express app and set middleware const port = 8080; const app = express(); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()) app.use( (req) => { req.rawBody = ""; req.setEncoding("utf8"); req.on("data", (chunk) => { req.rawBody += chunk; }); req.on("end", () => { req.next(); }); } ); // Generate JWT response const generateSignedResponse = (action) => { const signedRes = jwt.sign( action, privateKey, { algorithm: "RS256" } ); return signedRes } // Verify that the params of the rawTx match the payload const validateETHTransaction = (payload) => { try { // Decode RLP const unsignedTx = FeeMarketEIP1559Transaction.fromSerializedTx( Buffer.from( payload.rawTx[0].rawTx, "hex" )) return ( parseFloat(unsignedTx.value, 16) / 1e18 === payload.destinations[0].amountNative && unsignedTx.to.toString("hex") === payload.destinations[0].displayDstAddress.toLowerCase() && unsignedTx.getMessageToSign(true).toString("hex") == providedHash ) } catch (e) { console.log("Error:", e) } } // Tx Sign Request endpoint app.post("/v2/tx_sign_request", (req, res) => { try { let response; const tx = jwt.verify(req.rawBody, cosignerPubKey); if (validateETHTransaction(tx)) { response = generateSignedResponse({ action: "APPROVE", requestId: tx.requestId }) } else { response = generateSignedResponse({ action: "REJECT", requestId: tx.requestId, rejectionReason: `Failed to validate ETH transaction` }) } res.status(200).send(response); } catch(e) { console.error(e) res.sendStatus(401).send(); } }); console.log(`Callback is running on http://localhost:${port}`) app.listen(port); ``` # GCP Confidential Space API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/google-cloud/api-cosigner-maintenance-gcp-confspace > [!NOTE] > > You must have the necessary privileges to your Google Cloud account to perform maintenance operations. ## View the logs To view the Co-Signer's logs, go to the [GCP's VM instances page](https://console.cloud.google.com/compute/instances) and validate that you see the right project (for example, "GCP-Customer-Cosigner-Dev"). After that, click on the relevant VM name and then press "logging" (which appears under "Logs"). This will open a new window with the customer Co-signer's logs. ## Observe the status To check the Co-signer's health, run the following command: ```bash theme={"system"} gcloud compute instances describe --zone= | grep status ``` Search for the `status` field of the VM in the output and confirm it is set to `RUNNING`. However, this does not guarantee the health of the Co-signer. To verify its functionality, monitor the transaction activity passing through it. Also, use the [Co-signers management tab](https://support.fireblocks.io/hc/en-us/articles/17923671680540-The-Co-signer-management-tab) to observe the online / offline status. ## List the paired API users Use the [Co-signers management tab](https://support.fireblocks.io/hc/en-us/articles/17923671680540-The-Co-signer-management-tab) to observe the paired API users. ## Retrieve the public key You can view the Co-signer's logs in Google Cloud's console to find the public key used by the Callback Handler's server for JWT-encoded signed message authentication. Search for `public key` in the logs, as shown in the example below. ![](https://mintcdn.com/fireblocks-43c4b3ee/P9NyquD_UBc75wK-/images/docs/2149e195156b61df438b2fa99953f296af9e77da0d9c89c873f6b4837ae93488-image.png?fit=max\&auto=format\&n=P9NyquD_UBc75wK-\&q=85\&s=3ff51267e7b3aa2ae114519e89f798d2) ## Stop the Co-signer You can stop the Co-signer by running: ```bash theme={"system"} gcloud compute instances stop --zone= ``` > [!NOTE] > > This operation will not delete the VM. You can also stop the VM from the GCP console. ## Restart the Co-Signer In case the Co-signer's VM is down, given the Co-signer was already created, you can restart the Co-signer by running: ```bash theme={"system"} gcloud compute instances start --zone= ``` When you restart the Co-signer after installing it, it will use the same configuration (metadata), so you don't need to enter all the parameters again. ## Retrieve the running version Use Google Cloud's console to check the Co-signer's running image version by observing the `tee-image-reference` field in `Custom metadata` in the `Details` section. Alternatively, you can search for `Initializing CustomerCosignerGCPConfidentialSpace, commit sha` within the instance logs. ## **Update proxy settings** You can update or remove proxy settings on a running Co-signer without performing a full upgrade. The `--update-proxy` flag on the install/upgrade script handles this. It updates the VM's instance metadata in place and resets the VM to apply the change. No new VM is created. The operation causes approximately 2 minutes of downtime while the VM restarts. ### **Run the update** ```bash theme={"system"} ./client_install_upgrade_api_cosigner_script.sh --update-proxy cloud-resources-.yaml ``` Replace `cloud-resources-.yaml` with the resources file generated during your installation. #### **What the script does** 1. Loads your current configuration from the resources YAML file and displays a summary, including whether a proxy is currently configured. 2. Prompts you for the new proxy URL. To remove an existing proxy, press **Enter** without entering a value. 3. If a proxy URL is provided, encrypts it using your Cloud KMS key (credentials are never stored in plaintext), then prompts for `NO_PROXY` patterns. The current `NO_PROXY` value is shown as the default; press **Enter** to keep it. 4. Displays a summary of the changes and asks for confirmation before touching the running VM. 5. Updates the VM's instance metadata with the new configuration and resets the VM. 6. Writes the updated proxy settings to your resources YAML file. #### **Proxy URL format** ```http theme={"system"} http://proxy.example.com:8080 ``` To authenticate to the proxy, include credentials in the URL: ```http theme={"system"} http://username:password@proxy.example.com:8080 ``` #### **Effect on WebSocket vs. long-polling** When a proxy is configured, the Co-signer uses long-polling (REST) for communication with Fireblocks. WebSocket connections cannot reliably traverse proxies. When you remove a proxy, the Co-signer reverts to the WebSocket setting that was active before the proxy was added. #### **Verify the update** Once the VM is back online (approximately 2 minutes after the reset), proxy activity appears in Cloud Logging. To stream recent logs: ```bash theme={"system"} gcloud logging read 'resource.type=gce_instance AND labels.instance_name=YOUR_VM_NAME' \ --limit=50 \ --order=desc ``` Replace `YOUR_VM_NAME` with your Co-signer VM name (visible in the summary the script prints before confirmation). ## Update the Co-signer You can update the Co-signer by running the following script: ```bash theme={"system"} client_install_upgrade_api_cosigner_script.sh ``` > [!NOTE] > > If you're running a version earlier than 2026.05.27, the script may be called `client_stop_and_update_gcp_api_cosigner_script.sh`. This script performs the following: * Checks the required IAM bindings before upgrading, and offers to run any missing grant for you. * Stops the existing VM running the current Co-signer. * Pins the new image SHA and reference, so no manual SHA step is needed. * Updates the OIDC workload identity pool provider's attestation verification with the new image's SHA of the new version. * Creates a new VM that runs the new image, named `cosigner-YYYYMMDD-HHMMSS`, in the same zone as the previous VM. * Revokes the grants of the previous image digest after the new VM is created. The new Co-signer VM utilizes the same database, keys, and cloud resources as the previous one. When run with no arguments, the script detects the existing `cloud-resources` file and asks whether to Install or Upgrade. To upgrade non-interactively, run: ```bash theme={"system"} ./client_install_upgrade_api_cosigner_script.sh --upgrade cloud-resources-.yaml ``` > [!NOTE] > > * **Delete the old VM once the new one is fully operational.** Because the policy is configured to protect the VM from deletion, use the GCP console to disable the deletion protection under `VM > Edit > UNtag "Enable deletion protection"` and save the new settings before deleting the VM. > * If a proxy was configured when the Co-signer was installed, the upgrade carries that configuration forward automatically. No additional input is required. To change or remove proxy settings on a running Co-signer, use the `--update-proxy` flag instead of performing an upgrade. See the [Update proxy settings](/cosigners/google-cloud/api-cosigner-maintenance-gcp-confspace#update-proxy-settings) section. ## Migrate to a new machine Stop the current Co-signer VM and set up a new one using the same resources besides the VM. ## Configure the communication protocol WebSocket is the default protocol the Co-signer uses to communicate with the Fireblocks SaaS. You can switch to HTTP long polling when installing a new Co-signer, or by editing the metadata of an existing VM. > [!NOTE] > > If you're running a version earlier than 2026.05.27, the script may be called `client_install_gcp_api_cosigner_script.sh`. ### On a new VM The `client_install_upgrade_api_cosigner_script.sh` script handles the communication protocol for you; no script edits are required. * Interactive installs ask: *Do you plan to use a retry mechanism in the callback handler?* * **No** — the Co-signer uses WebSocket. * **Yes** — the Co-signer uses HTTP long polling. * Non-interactive installs read the `callback_retry_mechanism` field from the YAML. ### On an existing VM If you don't want to create a new VM, you can edit the existing VM's metadata and restart it to switch to HTTP long polling. * Stop the Co-signer VM. * From the GCP console, locate `VM > Edit > Metadata`. * Remove "`--websocket`" from the `Value 1` matching Key 1, which is `tee-cmd`. * Click Save and restart the VM to apply the change. # GCP Confidential Space API Co-signer Version History Source: https://developers.fireblocks.com/cosigners/google-cloud/api-cosigner-versions-gcp Fireblocks updates the API Co-signer software to include stability enhancements and new features. This article details the most recent Google Cloud Confidential Space Co-signer versions. Always use the latest version listed here for optimal performance. *** ## Latest version ### **Version 2026.10.21 (October 2026)** **Version hash:** `90324e06` * Callback enhancement: the number of change outputs is now included in the transaction metadata sent to the callback handler * Base image and root CA updates * Security fixes and updates *** ## Version history ### **Version 2026.10.03 (October 2026)** **Version hash:** `77774ae4` * Security and bug fixes ### **Version 2026.09.16 (September 2026)** **Version hash:** `0449f81f` * Fast signing for EdDSA * Base image updated * Security updates ### **Version 2026.08.19 (August 2026)** **Version hash:** `f5ab4d7f80` * KMS key decryption now uses a Workload Identity Pool binding instead of a Service Account binding * Callback responses may now be signed with ECDSA P-256 (ES256) in addition to RSA (RS256) * Security fixes ### **Version 2026.07.20 (July 2026)** **Version hash:** `86d1be9f9b` * Source and destination tags are now sent to the callback handler * Fixed an issue where Co-signer logs showed the password used for proxy authentication * Security improvements and bug fixes ### **Version 2026.06.24 (June 2026)** **Version hash:** `80e34d008a` * Performance improvements and bug fixes ### **Version 2026.05.27 (May 2026)** **Version hash:** `1b31e6b7fb` * Single script from installation and upgrade - `client_install_upgrade_api_cosigner_script.sh`. The old installation script has been removed. * Support for Fireblocks BCM * Support for batch attachment of tags * Performance improvements and bug fixes ### **Version 2026.04.27 (April 2026)** **Version hash:** `1d5b48eeef` * The cosigner can now connect through a proxy. User/password authentication and no\_proxy values are also supported. * Performance improvements, security enhancements and bug fixes ### Version 2026.03.02 (March 2026) **Version hash:** `95a317d3c7` * Added support for POLICY\_CHANGE approval request type (the customer co-signer can now approve policy changes via Policy Service v2) * Performance improvements and bug fixes ### Version 2026.01.29 (January 2026) **Version hash:** 340d9f6057 * Bug fixes and performance improvements ### Version 2025.12.30 (December 2025) **Version hash:** a294449c1b * Added support for "Protected Tags" approval requests ### Version 2025.12.11 (December 2025) **Version hash:** c44604398e * The callback can now return a “retry” response in an exchange withdraw operation ### Version 2025.08.12 (August 2025; US environments only) **Version hash:** 19c07421dc * Fixed WebSocket bug causing performance issues * Image digest SHA (US): sha256:61f951f0b8a66e906ab3ef742a3aa6b0312b279190c3e6478feae6d4b002be3f * Image path (US): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:2025.08.12 ### Version 2025.07.16 (July 2025) **Version hash:** bb61e507 * Fixed bugs and increased performance * Better support for web sockets * Image digest SHA (US): sha256:19b651f855f697ffc67ba97c33639f5a4c6db717019412df62e3ee5994ea6c44 * Image Path (US): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:2025.07.16 * Image details for EU: Digest SHA - sha256:4318bf2b9c0eccf8fd2cda2291366a6cf406e426c8a97b3d754a3d9bcc4e1d13 Path - us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:2025.07.16.eu * Image details for EU2: Digest SHA - sha256:1f11be136216311532d1277d7fe9e4aba3e4394c6f699904f8969aca097c3dc0 Path - us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:2025.07.16.eu2 * Image details for Sandbox: Digest SHA - sha256:5155a2258fba75b7321ca3136aab47f2af0a14dd0cdf29e0132ab06da1ab6086 Path - us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:2025.07.16.sandbox ### Version 1.1.1 (April 2025) **Version hash:** 7160d7b833 * Bug fixes and performance improvements ### Version 1.1.0 (March 2025) **Version hash:** 2584ed02d1 * Image SHA (US): sha256:40893bdf713df66f4cfa857420989b583c0011c84fee0b8f92acd182d37a7f81 * Image Path (US): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.1.0 * Image SHA (EU): sha256:2e4f02f5c90e92819111fd93822cf557ab1ffd5a8faed919bd3144ce0563e6cc * Image Path (EU): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.1.0.eu * Image SHA (EU2): sha256:af2178f2f80a64107b04d7627e3e0e2483c745f999fdf30f60c2808900ae40ff * Image Path (EU2): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.1.0.eu2 * Bug fixes and performance improvements ### Version 1.0.0 (January 2025) **Version hash:** 27de90bb6f * Image SHA (US): sha256:af6b7e6276af09e20e389ec2f63299bca293f646154e48b8144a2a9179c0b06e * Added helper script for updating an existing GCP Customer Co-Signer to a newer image * Validated + handled if missing physicalDeviceId in the Co-Signer’s config (relevant to all Co-Signers) * Added default image path and image SHA to GCP Customer Co-Signer scripts * Image SHA (US): sha256:af6b7e6276af09e20e389ec2f63299bca293f646154e48b8144a2a9179c0b06e Image Path (US): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.0.0 * Image SHA (EU): sha256:5c456aa3e5953e0fedfa80717d8cc977e9d9ee94eb561cbabe69379baa587386 * Image Path (EU): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.0.0.eu * Image SHA (EU2): sha256:861c5d90fdb345a794cea4a8ce2c886c2cb3a9d531b825da38cc27848b978a0c * Image Path (EU2): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.0.0.eu2 * Image SHA (Sandbox): sha256:18018d945f1bf7f8cd82273798d997c0bdef56db67a1c101c87d8e12aaf6df79 * Image Path (Sandbox): us-docker.pkg.dev/fireblocks-customer-cosigner/fireblocks-customer-cosigner-repository/gcp-cosigner:1.0.0.sandbox ### Version 0.9.5 (October 2024) **Version hash:** 121b4c2775 * Image SHA: sha256:894c4e573b52e785cf7b2ad2e10f73d766b4a88cb1078daf087ce8ab8703f928 * Security policy enhancements * Bug fixes ### Version 0.9.4 (November 2024; EU version) **Version hash:** d177e696c3 * Image SHA: sha256:30aca13b7dcb7db41a4138066ca3bc6e601ad1cdc417484f8c6a7433851eba62 * Added support for platform remote commands * Performance optimizations * Security policy enhancements * Bug fixes ### Version 0.9.4 (October 2024; non-EU version) **Version hash:** d177e696c3 * Image SHA: sha256:7ecfc480ccb65613db0f2b1cedfcd765be944bbadb5da1e0e098fa0ccb0876cb (Prod, not EU) * Added support for platform remote commands * Performance optimizations * Security policy enhancements * Bug fixes ### Version 0.9.3 (August 2024) **Version hash:** 6647aa5291 * Image SHA: sha256:ed12b926a7d46e98be9943791c4e350dd193a4bc5f7c222ce1c765715b11bf9e * Added printing the Co-signer Public Key to the logs ### Version 0.9.2 (July 2024) **Version hash:** 257b33246b * Image SHA: sha256:5bd371e9a877bcf7cc662ecaae5791b3b5c8b797348e1540faaf272cc153cb39 * Initial release # Google Cloud Confidential Space API Co-signer Architecture Source: https://developers.fireblocks.com/cosigners/google-cloud/gcp-confidential-space-api-co-signer > [!NOTE] > > Learn how to install GCP Confidential Space Co-signer in the [following guide](/cosigners/google-cloud/install-api-cosigner-gcp). ## GCP Co-signer can only connect to one workspace at a time Like other Co-signers, the Google Cloud Confidential Space Co-signer is installed and connected to the workspace through an API user. Once installation is complete, you can manage it via the Console or APIs to add additional API users, configure their Callback Handlers, etc. Due to Google Cloud's Confidential Space container architecture, the Co-signer can only be commanded through Fireblocks' SaaS. As a result, it is restricted to a single workspace connected to the Co-signer. All operations must be performed within this workspace using the Console or APIs. ## Google Cloud resources used by the Co-signer The Fireblocks GCP Confidential Space API Co-signer leverages Google's Confidential Space technology. It utilizes the following Google Cloud's resources: * **Workload Container**: used to run the enclave. * **Bucket**: used as the Co-signer's persistent storage and holds the encrypted database of the Co-signer. * **KMS Customer Managed Key**: used to securely protect the Co-signer's MPC keyshares, which are stored in the Co-signer's persistent storage within a bucket. * **IAM/WIP**: enables associating identities with Google Cloud's IAM roles to grant only essential permissions to specific resources in use. > [!WARNING] > > **Important**: Allocate a separate set of resources for each Co-signer to prevent conflicts and ensure isolation, enhancing security. The "project" can be common between different Co-signers. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/f70e5312c1ea20c45ac0700ab6156deeab529fd0e22851ef0b4030ac2a8f6644-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=e04ba9bd0f8060053585e826a4351e14) ## Secure Co-signer database encryption scheme The following process is implemented to build and secure the Co-signer's database: 1. The user creates and configures a symmetric Customer Managed Key (CMK) in the KMS. 2. The Co-signer, upon initialization, asks KMS to generate an additional AES 128-bit CBC key: `FBKS-DB-Key` 3. The Co-signer initializes its encrypted database using the key `FBKS-DB-KEY` 4. The Co-signer encrypts `FBKS-DB-KEY` using the CMK. 5. The Co-signer saves its encrypted database and the encrypted form of `FBKS-DB-KEY` in the GCS bucket, serving as its persistent storage. 6. Access to KMS, including the CMK, is restricted using a Fireblocks enclave image attestation signature and the IAM role and policies that were created by the user. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/6693015f92821789041e58ceabb5cc7d5b787d9371375f042d53c4a53aedfea8-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=f348352a525319b6c7607934c1135d45) # Install Google Cloud Confidential Space API Co-signer Source: https://developers.fireblocks.com/cosigners/google-cloud/install-api-cosigner-gcp ## Overview To install a Confidential Space Co-signer in Google Cloud and connect it to your workspace, follow these steps: 1. **Setup and configure your Google Cloud environment** Prepare your Google Cloud environment and the machine where you plan to install the Co-signer from (e.g., your laptop). 2. **Add a Co-signer to the workspace using an API user** Using the Fireblocks Console or APIs, create an API user and use it to add a Co-signer to the workspace. 3. **Install and connect the Co-signer to the workspace** Download the installation script to the machine where you plan to execute it, run the script to set up the necessary Google Cloud resources, and install the Co-signer. Once installation is complete, the workspace owner approves the new MPC key shares for the API user through the Fireblocks mobile app. You can now view the Co-signer and its paired API user in your Fireblocks Console. Additionally, you can retrieve information about them using the Co-signer APIs. ## Step 1: Set up and configure your Google Cloud environment Proper configuration of your Google Cloud environment is straightforward but must be performed step by step in the specified order. The installation script automates the resource creation and setup process and it is not recommended to change it. Any misconfiguration could compromise the Co-signer's functionality or security. ### 1.1. Allowlist Domains The Co-signer VM needs outbound access to the domains below on port 443, unless another port is listed. The **Needed for** column shows when each domain is used: * **Install, upgrade, and operation:** keep the domain open while the Co-signer runs. * **Install, upgrade, and VM restart:** used whenever the VM starts the Co-signer container. #### Fireblocks domains Fireblocks domains depend on the Fireblocks SaaS environment your workspace is connected to. | Purpose | Needed for | Global | Europe | Swiss | | - | - | - | - | - | | Fireblocks gateway | Install, upgrade, and operation | `mobile-api.fireblocks.io` | `eu2-mobile-api.fireblocks.io` | `eu-mobile-api.fireblocks.io` | | Certificate service | Install, upgrade, and operation | `s3signurl.fireblocks.io` | Not used | Not used | #### Google Cloud domains | Domain | Needed for | Used to | | - | - | - | | `us-docker.pkg.dev` | Install, upgrade, and VM restart | Pull the Co-signer container image | | `gcr.io` | Install, upgrade, and VM restart | Pull the Co-signer container image | | `confidentialcomputing.googleapis.com` | Install, upgrade, and operation | Get the Confidential Space attestation token | | `sts.googleapis.com` | Install, upgrade, and operation | Exchange the attestation token for Google Cloud credentials | | `iamcredentials.googleapis.com` | Install, upgrade, and operation | Get service account credentials | | `cloudkms.googleapis.com` | Install, upgrade, and operation | Encrypt and decrypt the Co-signer's data | | `storage.googleapis.com` | Install, upgrade, and operation | Store and load the Co-signer's encrypted data in your bucket | | `www.googleapis.com` | Install, upgrade, and operation | Call Google Cloud APIs | | `logging.googleapis.com` | Install, upgrade, and operation | Send the Co-signer container logs to Cloud Logging | | `privateca-content-*.storage.googleapis.com` (ports 80 and 443) | Install, upgrade, and operation | Check Google certificate revocation lists during attestation | The VM also uses the Compute Engine metadata server (`metadata.google.internal`), which is local to the VM and isn't reached over the internet. #### Your workstation The installation script runs on your workstation, not on the Co-signer VM. It calls the Google Cloud APIs through `gcloud`, and you download the installation package to it in [Step 3.1](#3-1-download-and-unpack-the-installation-package). The package download domain is `fb-prod-us-customers.s3.amazonaws.com` (Global), `fb-eu2-prod-customers.s3.amazonaws.com` (Europe), or `fb-eu-prod-customers.s3.amazonaws.com` (Swiss). > [!NOTE] > > If you cannot support DNS IP whitelisting with an advanced firewall or within the cloud itself, you must allow egress traffic to any destination on port 443. ### 1.2. Install the required software packages Ensure the following software packages are installed on the machine where you will run the installation script (e.g., your laptop): * [**gcloud**](https://cloud.google.com/sdk/gcloud): The installation script automates the setup process by using gcloud and prompts you for the required inputs. It assumes you have the necessary credentials and permissions for your Google Cloud account. **gcloud sdk version of 396+ is required.** * **uuidgen**: used for generating the Co-signer’s unique ID during installation. * **python3**: used by the installation script to read and write its configuration files. The installation script uses those packages to create the resources and install the Co-signer. ### 1.3. Ensure account permissions for the required Google Cloud resources You must have the necessary Google Cloud account permissions to enable network access to required domains and to create and configure the following resources: * Project * IAM Role * Workload Identity Pool provider * Customer Managed Key (CMK) * Bucket * Workload Container ### 1.4. Additional security recommendations It is highly recommended to control user and network access to your Google Cloud environment. See [API Co-signer security checklist and recommended defense and monitoring systems](/cosigners/overview/co-signer-security-checklist-defense-monitoring) for further information. See [Google Cloud recommendations](#google-cloud-recommendations) for audit logging, IAM, storage, and firewall settings. ## Step 2: Add a Co-signer to the workspace using an API user Follow the instructions to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Ensure you copy to your clipboard the following items, which you will use during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script ## Step 3: Install and connect the Co-signer to the workspace ### 3.1. Download and unpack the installation package Using the download link of the GCP Co-signer installation package you copied from the Console, run the `wget` command to download the package directly to your machine. You can find the download link in the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). Paste the appropriate URL into the following command: `wget -O gcp-cosigner.zip "URL"` Unpack the installation package by running the following command: `tar -xzvf gcp-cosigner.zip` ### 3.2. Run the installation script 1. Log in to gcloud with `gcloud auth login`. 2. The installation package contains an installation script. To install the Co-signer, navigate to the directory where the installation package was unpacked and run `client_install_upgrade_api_cosigner_script.sh` script in an interactive shell and respond to prompts as the script executes. > [!NOTE] > > If you're running a version earlier than 2026.05.27, the script may be called `client_install_gcp_api_cosigner_script.sh`. 3. When prompted, select option **"1 - Create"** for first-time setup. ### VM machine type The installation script provisions the Co-signer VM with an N2D machine type. The script specifies `--min-cpu-platform="AMD Milan"` when creating the VM to enable live migration, and Google Cloud provisions an N2D machine type for Confidential VM instances running on that CPU platform. For more information, see [Live migration for Confidential VM instances](https://docs.cloud.google.com/confidential-computing/confidential-vm/docs/troubleshoot-live-migration) and [Supported configurations](https://docs.cloud.google.com/confidential-computing/confidential-vm/docs/supported-configurations) in the Google Cloud documentation. ### 3.3. Approve MPC key shares for the API user If the API user used to pair with the Co-Signer and connect it to your workspace has an Admin or Signer role, the workspace owner will receive a notification. This notification prompts them to approve a new MPC key share request for that API user in the Fireblocks mobile app. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. [Observe that it is online](/cosigners/google-cloud/api-cosigner-maintenance-gcp-confspace) and that the API user is paired to it. ## **Proxy configuration (optional)** The Co-signer can route outbound traffic through an HTTPS proxy. When a proxy is configured, the Co-signer automatically switches from WebSocket to long-polling (REST), since WebSocket connections cannot reliably traverse proxies. The script prompts you for a proxy URL during installation. If you don't need a proxy, press **Enter** to skip this step. ```text theme={"system"} Please enter HTTPS proxy URL [can be empty; e.g. http://proxy:8080 or http://user:pass@proxy:8080]: ``` To authenticate to the proxy, include the credentials directly in the URL: ```http theme={"system"} http://username:password@proxy.example.com:8080 ``` The proxy URL — including any credentials — is encrypted immediately using your Cloud KMS key and is never stored in plaintext. Only the encrypted value is written to the `cloud-resources-*.yaml` file. If you provide a proxy URL, the script also prompts for `NO_PROXY` patterns: a comma-separated list of hosts that should bypass the proxy. The recommended defaults are: ```text theme={"system"} 169.254.169.254,metadata.google.internal,googleapis.com ``` These cover the GCP metadata server and Google APIs, which must remain reachable directly. **For non-interactive installs**, set these fields in your `install-config.yaml`: ```yaml theme={"system"} # Optional proxy fields proxy_url: http://username:password@proxy.example.com:8080 no_proxy: 169.254.169.254,metadata.google.internal,googleapis.com ``` ## Google Cloud recommendations * **Enabling Cloud Audit Logging**\ Cloud Audit Logging is critical for tracking administrative and data-access operations at the project level. As Fireblocks’ Co-Signer is deployed within customer-managed environments, customers are responsible for enabling and managing Cloud Audit Logging to meet their governance and monitoring needs. * **Cloud IAM - Service Account with Admin Privileges**\ GCP creates a default administrative service account during the initial setup. To reduce the attack surface and adhere to the principle of least privilege, customers should delete this service account after installation unless explicitly required for operational purposes. * **Cloud Storage - Enabling Buckets with Versioning**\ Versioning is primarily designed for backup and replication use cases. Fireblocks’ Co-Signer solution does not require this feature, and this configuration has no associated security risk. Customers may enable versioning based on their organizational backup and recovery strategies. * **VPC Enabling Subnet Flow Logs**\ As with AWS, enabling VPC subnet flow logs in GCP provides visibility into network traffic but may incur additional costs. Customers should consider enabling this feature if their security or compliance policies necessitate detailed network traffic analysis. * **API Event Monitoring**\ The Fireblocks Co-Signer is hosted within customer environments, and customers are responsible for implementing and managing API event monitoring. Fireblocks recommends monitoring API activity to detect anomalies and ensure compliance with security standards. * **Firewall Rules**\ GCP creates default firewall rules during project setup, which may not align with security best practices. Customers are strongly encouraged to delete these default rules and implement custom firewall rules that adhere to the principle of least privilege and align with their security requirements. # Install SGX IBM Cloud API Co-signer Source: https://developers.fireblocks.com/cosigners/ibm/install-api-cosigner-ibm ## Overview To install an SGX Co-signer in IBM Cloud and connect it to your workspace, follow these steps: 1. **Setup and configure your IBM Cloud environment** Prepare your IBM Cloud environment by creating and configuring the required resources. Ensure it meets the necessary specifications and security settings. 2. **Add a Co-signer to the workspace using an API user** Using the Fireblocks Console or APIs, create an API user and use it to add a Co-signer to the workspace. 3. **Install and connect the Co-signer to the workspace** Download the installation script to the SGX-capable virtual machine and run the script to install the Co-signer. Once installation is complete, the workspace owner approves the new MPC key shares for the API user through the Fireblocks mobile app. You can now view the Co-signer and its paired API user in your Fireblocks Console. Additionally, you can retrieve information about them using the Co-signer APIs. *** ## Step 1: Setup and configure your IBM Cloud environment ### 1.1. Allowlist domains To ensure the Co-signer can be installed and operated successfully, add the following domains to your allowlist: | Domain | Owner | | - | - | | `mobile-api.fireblocks.io` | Fireblocks | | `signurl.fireblocks.io` | Fireblocks | | `s3signurl.fireblocks.io` | Fireblocks | | `fb-certs.s3.amazonaws.com` | AWS | | `fb-cosigner-images.s3.amazonaws.com` | AWS | | `fb-customers.s3.amazonaws.com` | AWS | | `fb-customers.s3.amazonaws.com/uploads` | AWS | | `download.docker.com` | Docker | | `registry.gitlab.com` | GitLab | | `cdn.registry.gitlab-static.net` | GitLab | | `gitlab.com` | GitLab | | `github.com` | GitHub | | `download.01.org` | Intel SGX Driver | | `bootstrap.pypa.io` | Python Software Foundation | | `files.pythonhosted.org` | Python Software Foundation | | `pypi.org` | Python Software Foundation | | `pypi.python.org` | Python Software Foundation | Fireblocks-owned domains differ based on the specific Fireblocks SaaS environment you are connected to. If you are connected to the European or Swiss SaaS, update your allowlist according to the domains listed in the table below: | Fireblocks SaaS | Domains to Allow | | - | - | | Global | `mobile-api.fireblocks.io` `signurl.fireblocks.io` `s3signurl.fireblocks.io` | | Europe | `eu2-mobile-api.fireblocks.io` `eu2-signurl.fireblocks.io` `eu2-s3signurl.fireblocks.io` | | Swiss | `eu-mobile-api.fireblocks.io` `eu-signurl.fireblocks.io` `eu-s3signurl.fireblocks.io` | Additionally, ensure port access is configured for the following services: | Port | Service URL | | - | - | | 443 | `https://mobile-api.fireblocks.io` | | 443 | `https://s3signurl.fireblocks.io` | | 443 | `https://fb-certs.s3.amazonaws.com` | | 443 | `https://fb-customers.s3.amazonaws.com/uploads/` | | 443 | `https://bootstrap.pypa.io/get-pip.py` | | 443 | `https://download.docker.com/linux` | | 443 | `https://download.01.org/intel-sgx/` | | 5000 | `https://registry.gitlab.com/customer-cosigner` | > [!NOTE] > > If you cannot support DNS IP whitelisting with an advanced firewall or within the cloud itself, you must allow egress traffic to any destination on port 443. ### 1.2. Create an SGX virtual machine **The minimum hardware requirements for the VM are:** * RAM: 32GB * Storage: 256GB * OS: * Ubuntu 22.04 LTS, 24.04 LTS, or 26.04 LTS (Canonical). Ubuntu 26.04 LTS requires API Co-signer version 2026.10.21 or later. * Latest Linux kernel version * Latest Intel microcode (BIOS update) Complete the following steps to create an SGX-capable VM in IBM Cloud: 1. On the Dashboard page, select **Create Resource**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/1e9a3df5b35aca345ebc5e17c6d11f396681f11a8c42c4511b4edf163a57acd8-Screenshot_2024-12-06_161558.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=ed177a8968fe8261e9a08e6b910dbb08) 2. Go to **IBM Cloud catalog** > **Compute** > **Bare Metal Servers**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/b884e4f8df0d02f9663dce7ed397e6344f89021ac14f327ac407f957cf6497b7-Screenshot_2024-12-06_161647.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=4e07db986b30d62b29ade17ea1603171) ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/3d53339df7c54c843f7e7e2f719eda00408ce23e9ebcfb84bef3e3805fffca54-Screenshot_2024-12-06_161709.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=7be343ebdc1bd061e9ac2d014e5f6189) 3. In the Server Profile section, select **View all profiles**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/7cfcef2372aaa7c61e80c173562fa512078073b23d8343f44214c8b782321742-Screenshot_2024-12-06_161737.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=3080e5d5f7909f121c6e0b6fee166817) 4. Select **Intel Xeon E-2174G CPU**. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/495a466f6189c2bd24671bc227402b69d7e4c739cc7a01e276f4f1b6a3c966f8-Screenshot_2024-12-06_161911.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=17e04f3afd1b2df5974d2a0d3deee23f) 5. In the Operating System section, select the following options: 1. Vendor: Ubuntu 2. Version: 18.04 LTS (64-bit) 3. RAM (recommended): 32 GB ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/449fb3a17b0fb4224bdbc3582c644ec51619461973a54d99390b564befdfffbc-Screenshot_2024-12-06_162045.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=10ee68f0bcb95ebd230d6dc1ecc20826) 6. Select the **Software Guard Extensions** toggle under Add-ons > **Security and Business Continuity**. ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/fa2ff87006b490f4aa93dc6711f2c0b1ce61468af2723ccdfd5d5b1fb4445024-Screenshot_2024-12-06_162120.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=0927ef101e2064037d6ee301ebcc6e7d) 7. Lastly, create the VM. ### 1.3. Verify SGX is enabled on your VM After creating your virtual machine, confirm that SGX is enabled. The SGX Co-signer requires a server with SGX enabled and the latest patches applied. This verification ensures smooth operation and avoids potential issues. To verify that SGX is enabled on the VM, run the following commands with root privileges:: ```bash theme={"system"} apt update apt upgrade apt install cpuid cpuid -1 | grep -i sgx ``` Verify the following: 1. `SGX`: Software Guard Extensions supported is **true** 2. `SGX_LC`: SGX launch config supported is **true** ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/fe23ee1939feee21b46aa47061c3d01ab28840989133235e3d3b578d3722630a-Screenshot_2024-12-06_133814.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=79db17bab8bfbeff948a5374cf9a8363) ### 1.4. Additional security recommendations It is highly recommended to control user and network access to Co-signer's machine. See [API Co-signer security checklist and recommended defense and monitoring systems](/cosigners/overview/co-signer-security-checklist-defense-monitoring) for further information. *** ## Step 2: Add a Co-signer to the workspace using an API user Follow the instructions to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Ensure you copy to your clipboard the following items, which you will use during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script *** ## Step 3: Install and connect the Co-signer to the workspace > [!NOTE] > > You must have root privileges on the Co-signer machine to install the Co-signer. Ensure you are logged in as a root user or use `sudo` to execute the commands. ### 3.1. Download the installation script Using the download link of the SGX Co-signer installation script you copied from the Console, run the `curl` command to download the package directly to your machine. You can find the download link in the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). Paste the appropriate URL into the following command: ```bash theme={"system"} curl -o cosigner "URL" ``` ### 3.2. Run the installation script After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` To install the Co-signer, run: ```bash theme={"system"} ./cosigner setup ``` You will be prompted to enter the **Pairing token** for the API user, which you retrieve from the Fireblocks Console. This token pairs the API user with the Co-signer. At this stage, you will have the option to configure the Callback Handler parameters for the API user connecting the Co-signer to the workspace. This feature is optional. You can [configure it later through the Console, APIs, or locally](/cosigners/operations/api-cosigner-operate) from the Co-signer's host machine. For detailed instructions on setting up your Callback Handler's interface to the Co-signer and implement its logic and code, refer to the [Setup API Co-signer Callback Handler](/reference/api-cosigner-setup-callback-handler) section. The setup process validates the machine's hardware and installs the necessary drivers to support the appropriate SGX version and the SGX Co-signer's executable image. It includes an attestation flow to ensure that the SGX Co-signer's executable runs securely inside an Intel SGX enclave. Additionally, the script installs other required components. Once the installation is complete, the Co-signer will automatically start running. At the end of the process, the Co-Signer generates a JSON configuration file, which can be used for future configuration updates. ### 3.3: Approve MPC key shares for the API user If the API user used to pair with the Co-Signer and connect it to your workspace has an Admin or Signer role, the workspace owner will receive a notification. This notification will prompt them to approve a new MPC key share request for that API user using the Fireblocks mobile app. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. Observe it is online and that the API user is paired to it. *** > [!NOTE] > > To check the Co-signer's status and observe the logs, see the SGX Co-signer Maintenance article. # Intel SGX Co-signer Architecture Source: https://developers.fireblocks.com/cosigners/ibm/sgx-architecture-ibm > [!NOTE] > > Learn how to install SGX Co-signer in IBM Cloud in the [following guide](/cosigners/ibm/install-api-cosigner-ibm). ## Resources used by the SGX Co-signer The Fireblocks SGX Co-signer utilizes Intel's SGX enclave and attestation mechanisms. It can be deployed either in cloud service providers that support compatible SGX servers or on-premise using a bare-metal server. Also, an SGX driver must be loaded into the machine. This is the only resource that is required to install and operate the Co-signer on the host machine. The Co-signer's database is encrypted and stored on the host machine's disk, serving as the Co-signer's persistent storage. > [!WARNING] > > **Important**: Allocate a separate machine for each Co-signer to prevent conflicts and ensure isolation, enhancing security. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/1ac3082630bdf2681904ab3d00f491f79bd5f8ef4025df3d6c3958eb9342566f-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=e03c764718b4c83de4c47a4d3cbe1bdd) ## Secure Co-signer database encryption scheme The following process is implemented to build and secure the Co-signer's database: 1. The Co-signer, upon initialization, connects to the [Intel-SGX Remote Attestation server](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/attestation-services.html) that belongs to the Fireblocks SaaS and retrieves the key that encrypts its database: `FBKS-DB-Key` 2. The Co-signer initializes its encrypted database using the key `FBKS-DB-KEY` 3. The Co-signer encrypts `FBKS-DB-KEY` using the SGX hardware key of the host machine. 4. The Co-signer saves its encrypted database and the encrypted form of `FBKS-DB-KEY` in the host machine, serving as its persistent storage 5. The Co-signer cannot operate unless it is attested against Fireblocks' Remote Attestation server. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/a3cd183923542641d62c25dfe6000bc85c4c2db7a2151bdbb51ef19b31dda23b-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=a0bbb047d06aa91ed17bdcd7c70afafb) ## Installation artifacts The SGX Co-signer is comprised of several components: 1. Installation script: This is the CLI interface for installing and managing the Co-signer. You can retrieve the installation script from the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). The script includes the path to a Docker image registry for downloading the Co-signer Docker image. It supports downloading images of various versions, with default versions specified if none are explicitly provided. See the [SGX Co-signer version history](/cosigners/sgx/api-cosigner-versions-sgx) for details on the image version. 2. Docker image: The Co-Signer's executable is wrapped in a Docker image to provide safe, versioned, and consistent installation across different platforms. The Docker image provides all the libraries and dependencies of the main executable. 3. Main loading executable: A slim binary that downloads and verifies the enclave and exposes command line interfaces. 4. Secure SGX enclave: This component contains most of the Co-Signer's code and logic. It is both encrypted and signed by Fireblocks, and it can be downloaded from the Fireblocks server during setup. * **Q: Should I verify the executable's signature?**\ If you are using the correct Docker image, as specified by the script, there is no need for any other verification, as it is embedded in the image. * **Q: Why is the enclave encrypted?**\ SGX allows for saving the enclave encrypted on disk with a hardware key only available to the specific CPU where the enclave would be executed. Secured enclaves prevent the risk of modification, make it impossible to reverse engineer, and hide any potential secrets embedded within them. This enclave alone is capable of reading the secrets stored by the Co-Signer. * **Q: Should I verify the enclave's signature?**\ There is no need for that. The Fireblocks signing key is embedded within the loading executable, which verifies the enclave's signature whenever it is loaded. * **Q: Where do I find the enclave in the docker image?**\ The enclave is only downloaded when the Co-signer goes through a setup or upgrade process, so it is omitted from the Docker image. The name of the file is `enclave.signed.so`. The chain of trust follows the sequence: **script > image > executable > enclave**, with Fireblocks ensuring compatibility between these components. During setup or upgrade, the Co-signer downloads the latest enclave version from Fireblocks servers, which is guaranteed to be compatible with the installed executable. To ensure the enclave uses the latest published version, always use the most recent Co-signer Docker image. The safest approach is to install the Co-signer using the latest installation script. # SGX API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/ibm/sgx-maintenance-ibm > [!NOTE] > > You must have root privileges on the Co-signer machine to perform maintenance operations. Ensure you are logged in as a root user or use `sudo` to execute the commands. ## View the logs You can export the logs to a file in the local directory, tagged with the current date and time, by running the following command: ```bash theme={"system"} ./cosigner logs ``` Append a number to the command to retrieve the specified most recent amount of lines. By default, the Co-signer uses the Docker logging container. However, for better monitoring, the logs can be streamed to an external log delivery service, such as ELK, Splunk, or Datadog. The SGX Co-signer generates two different types of logs for troubleshooting and auditing: * `run.log` is an automatically generated log containing all of your Co-signer setup and configuration history. * `customer_cosigner.log` contains workspace operation data, such as the workspace's transaction approving and signing history and new connection approvals. These log files are separated into 10 files limited to 4.1MB each. They are automatically overwritten in chronological order. Your API Co-signer logs are structured using the following Regular Expression (RegEx): ```bash theme={"system"} ${AppName}:${Line} ${LogLevel} ${dd/MM/yyyy} ${HH:mm:ss,fff} ${ClassName} ::${MethodName} - ${LogMessage} ``` These logs are collected by the Docker container’s `STDOUT` stream and written into a log file on the container’s host machine at the location specified below. These log files are rotated with a max file size of 4MB for each log file. ```text theme={"system"} /databases/cosigner/log/customer_cosigner.log /databases/cosigner/log/customer_cosigner.log.1 /databases/cosigner/log/customer_cosigner.log.2 ... ``` > [!NOTE] > > The Co-signer is Docker-based and uses the Docker JSON file driver for logging. By default, the driver uses a standard logging output but can be configured to support other logging mechanisms. Refer to the official Docker documentation for more detailed information. *** ## Verify SGX is enabled on the machine After completing your server creation or installation, verify that SGX is enabled on the server. The Fireblocks API Co-Signers can only run on SGX-enabled servers with the latest patches, and verifying your configuration helps ensure you do not run into any potential issues later on. 1. Run the following commands on the server: ```bash theme={"system"} sudo apt update sudo apt upgrade sudo apt install cpuid cpuid -1 | grep -i sgx ``` 2. Verify the following: 1. SGX: Software Guard Extensions supported is `true`. 2. SGX\_LC: SGX launch config supported is `true`. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/0c0a9cb9db9029ad2559a58ddcde807472c7674dc0a81d25cd6dd58f57f37c5b-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=128b5d0f685e48863e18cc368d7d140b) > [!NOTE] > > Azure Standard\_DC\*\_v2 instances do not support SGX2, but it is not necessary to run the signer. *** ## List the paired API users You can list all API users paired with the Co-signer across the connected workspaces by running the following command: ```bash theme={"system"} ./cosigner list-users ``` The output will display a list of all API users paired with your Co-Signer, including the workspace name they are connected to, the API user's ID (its API key), and the associated Callback Handler server URL (if applicable). *** ## Retrieve the public key You can retrieve the Co-signer's public key, used by your optional Callback Handler server to authenticate requests from the Co-signer, by running the following command: ```bash theme={"system"} ./cosigner print-public-key ``` *** ## Stop the Co-signer You can stop the Co-signer by running the following command: ```bash theme={"system"} ./cosigner stop ``` *** ## Start the Co-signer You can Start the Co-signer by running the following command: ```bash theme={"system"} ./cosigner start ``` *** ## Restart the Co-Signer You can restart the Co-signer by running the following command: ```bash theme={"system"} ./cosigner restart ``` *** ## Retrieve the running image version You can retrieve your running Co-Signer version by running the following command: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image' + " " + '.[0].Image' ``` *** ## Retrieve the script version You can retrieve your current Co-Signer script version by running the following command: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` *** ## Update the Co-signer In the Fireblocks Console, go to [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer) and copy the download link of the Intel SGX installation script. The link is valid for seven days. Use the `curl` command to download the script to the path where the existing Co-Signer script is present. Paste the copied URL into the following command: ```bash theme={"system"} curl -o cosigner "URL Download Link" ``` > [!NOTE] > > If you have any issues with finding the installation script URL, please contact [Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=360003372200\&tf_360023089139=global_settings\&tf_360023089159=get_api_co-signer_installation_script). After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` Stop the Co-signer container by running the following command: ```bash theme={"system"} ./cosigner stop ``` Run `docker ps -a` and check the output. There should be no Co-signer container running. Backup the Co-signer's database by running the following command: ```bash theme={"system"} mv /databases/cosigner/enclave/enclave.signed.so /databases/cosigner/enclave/enclave.signed.so_old ``` Remove the current revision file: ```bash theme={"system"} sudo rm -f .revision ``` You can now update the Co-signer to the latest version by running the following command: ```bash theme={"system"} ./cosigner start ``` You can update to a specific image version by replacing the `` : ```bash theme={"system"} ./cosigner start ``` Run `docker ps -a` and check the output. You should see the Co-signer container running. If the update process did not succeed, try re-installing the Co-signer using the same version, but first remove the current version of the Co-Signer by running the following commands: ```bash theme={"system"} sudo rm -rf /databases sudo rm -f .local_config .revision ``` and then run: ```bash theme={"system"} ./cosigner setup ``` If that still doesn't work, refer to [API Co-signer Troubleshooting](/cosigners/operations/api-cosigner-troubleshooting). *** ## Migrate to a new machine Begin by setting up a new SGX-capable machine in your cloud or on-premises environment. Migrating the SGX Co-Signer to a new machine is most effectively performed using backup data from the existing machine. This approach minimizes downtime and reduces the risk of interruptions to your workspace operations. The following files and folders are generated automatically during the SGX Co-signer setup and are essential for backup and recovery purposes: 1. `/databases/cosigner/db`: The Co-signer DB folder stores the `secrets.db` file, the Co-Signer's secrets database. This file is encrypted with a key generated in a secure SGX enclave. 2. `/databases/cosigner/backup`: The backup folder maintained by the Co-Signer stores the backup upon any change to the database. The files are formatted using the following timestamp: `%Y%m%d%H%M%S.db` (e.g. `20220710184350.db`) based on the backup's date. 3. `/databases/cosigner/enclave/ra_loader_enclave.signed.so` - The enclave loader. Backup the files `secrets.db` and `ra_loader_enclave.signed.so` and copy them to the same folder locations on the new server: * `/databases/cosigner/db/secrets.db` * `/databases/cosigner/enclave/ra_loader_enclave.signed.so` After the above files have been copied to the new server, run `./cosigner start`. If you don't have backups of either `secrets.db` or `ra_loader_enclave.signed.so` files, unpair (re-enroll) the API users that are associated with the Co-signer and set up a new Co-signer on the new machine. Install the replacement on [Azure](/cosigners/azure/install-api-cosigner-azure), [Azure Marketplace](/cosigners/azure/install-api-cosigner-azure-marketplace), [IBM Cloud](/cosigners/ibm/install-api-cosigner-ibm), [Alibaba Cloud](/cosigners/alibaba/install-api-cosigner-alibaba), or [on-premises](/cosigners/on-premises/install-api-cosigner-onprem). # SGX API Co-signer Version History Source: https://developers.fireblocks.com/cosigners/ibm/sgx-versions-ibm Fireblocks updates the API Co-signer software to include stability enhancements and new features. This article provides details about the most recent SGX Co-signer versions. Always use the latest version listed here for optimal performance. ## Latest SGX Co-signer image version ### **Version 2026.10.21 (October 2026)** **Version hash:** `90324e06` * Callback enhancement: the number of change outputs is now included in the transaction metadata sent to the callback handler * Base image and root CA updates * Security fixes and updates * Support RHEL 9 and Ubuntu 26.04 hosts *** ## SGX Co-signer image version history ### **Version 2026.10.03 (October 2026)** **Version hash:** `77774ae4` * Security and bug fixes ### **Version 2026.09.16 (September 2026)** **Version hash:** `0449f81f` * Fast signing for EdDSA * Base image updated * Security updates ### **Version 2026.08.19 (August 2026)** **Version hash:** `f5ab4d7f80` * Callback responses may now be signed with ECDSA P-256 (ES256) in addition to RSA (RS256) * Security fixes ### **Version 2026.07.20 (July 2026)** **Version hash:** `86d1be9f9b` * Source and destination tags are now sent to the callback handler * Security improvements and bug fixes ### **Version 2026.06.24 (June 2026)** **Version hash:** `80e34d008a` * Performance improvements and bug fixes ### **Version 2026.05.27 (May 2026)** **Version hash:** `1b31e6b7fb` * Support for Fireblocks BCM * Support for batch attachment of tags * Performance improvements and bug fixes ### **Version 2026.04.27 (April 2026)** **Version hash:** `1d5b48eeef` * Performance improvements, security enhancements and bug fixes ### Version 2026.03.02 (March 2026) **Version hash:** 95a317d3c7 * Added support for POLICY\_CHANGE approval request type (the customer co-signer can now approve policy changes via Policy Service v2) * Performance improvements and bug fixes ### Version 2026.01.29 (January 2026) **Version hash:** 340d9f6057 * Bug fixes and performance improvements ### Version 2025.12.30 (December 2025) **Version hash:** a294449c1b * Added support for "Protected Tags" approval requests ### Version 2025.12.11 (December 2025) **Version hash:** c44604398e * The callback handler can now authenticate using a certificate signed by a root CA certificate preloaded to the Co-signer * The callback handler can now authenticate using both JWT and a certificate provided directly to the Co-signer (or signed by a root CA certificate provided to the Co-signer) * The callback can now return a "retry" response on exchange withdraw operation ### Version 2025.10.09 (October 2025) **Version hash:** 4e34888c01 * Fixed bug parsing messages after websocket reconnect. * Fixed bug sending messages on half-closed websocket connections. ### Version 2025.08.12 (August 2025; US environments only) **Version hash:** 19c07421dc * Fixed WebSocket bug causing performance issues ### Version 2025.07.16 (July 2025) **Version hash:** bb61e507 * Fixed bugs and increased performance * Better support for web sockets ### Version 3.10.1 (April 2025) **Version hash:** 7160d7b833 * Bug fixes and performance improvements ### Version 3.7.1 (October 2024) **Version hash:** 4ec196aec5 * Decreased signing latency, along with general performance increases * Remote management capabilities through Fireblocks Console * Support for more types of approval requests * Various fixes and improved handling of internal errors * Timing details are logged after every outgoing network request * The cosigner version will be reflected in the HTTP User-Agent header ### Version 3.6.5 (February 2024) **Version hash:** 3b807e2af7 * Improved logging for easier problem-solving * Improved stability, security, and performance * Enhanced deployment flexibility * Added support for the Callback Handler retry mechanism, enabling the Callback Handler to respond asynchronously * Updated major and minor dependencies * Preliminary support for upcoming features * Bug fixes ### Version 3.5.0 (August 2023) * [MPC-CMP security enhancements](https://support.fireblocks.io/hc/en-us/articles/10167020461596-MPC-CMP-security-enhancements-version-update-August-2023) * Bug fixes ### Version 3.3.0 (January 2022) * Added support for HTTP network proxy on outbound requests * Bug fixes *** ## Latest SGX Co-signer script version ### Version 2025.10.11 (December 2025) * New default Docker image: 2025.10.11 ## SGX Co-Signer script version history ### Version 2025.10.09 (October 2025) * New default Docker image: 2025.10.09 * Switched to Calendar Versioning to align script versions with default image versions ### Version 1.1.13 (August 2025) * New default Docker image 2025.08.12 ### Version 1.1.11 (April 2025) * New default Docker image 3.10.1 ### Version 1.1.10 (January 2025) * New default Docker image 3.7.1 ### Version 1.1.9 (February 2024) * New default Docker image 3.6.5 ### Version 1.1.8 (January 2024) * Added support for Callback Handler configuration via Azure Marketplace * Same default Docker image as in image version 3.5.0 and used as in script version 1.1.7 ### Version 1.1.7 (August 2023) * New default Docker image 3.5.0 ### Version 1.1.6 (February 2023) * Added support for Docker version 1.29.2 * Added validation for microcode: * Additional information about the microcode version appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem * Added validation to make sure Hyper-Threading is disabled * If Hyper-Threading is enabled, additional information appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem ### Version 1.1.5 (February 2022) * New default Docker image 3.3.0 * Added support for Co-signers in high-availability * Updated authentication model * Added support for HTTP network proxy on outbound requests *** ## Checking SGX Co-signer versions ### How do I check the version of my running SGX Co-signer image? To check your running SGX Co-signer image version, run the following command on your VM: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image +" " + . [0].Image' ``` ### How do I check the version of my running SGX Co-signer script? To check your running SGX Co-signer script version, run the following command on your VM: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` # Install SGX On-prem API Co-signer Source: https://developers.fireblocks.com/cosigners/on-premises/install-api-cosigner-onprem ## Overview To install an SGX Co-signer on-premises and connect it to your workspace, follow these steps: 1. **Setup and configure your on-prem environment:** Prepare your on-prem environment by creating and configuring the required resources. Ensure it meets the necessary specifications and security settings. 2. **Add a Co-signer to the workspace using an API user:** Using the Fireblocks Console or APIs, create an API user and use it to add a Co-signer to the workspace. 3. **Install and connect the Co-signer to the workspace:** Download the installation script to the SGX-capable machine, then run it to install the Co-signer. Once installation is complete, the workspace owner approves the new MPC key shares for the API user through the Fireblocks mobile app. You can now view the Co-signer and its paired API user in your Fireblocks Console. Additionally, you can retrieve information about them using the Co-signer APIs. ## Step 1: Set up and configure your on-prem environment and SGX server ### Step 1.1: Allowlist domains To ensure the Co-signer can be installed and operated successfully, add the following domains to your allowlist: | Domain | Owner | | - | - | | `mobile-api.fireblocks.io` | Fireblocks | | `signurl.fireblocks.io` | Fireblocks | | `s3signurl.fireblocks.io` | Fireblocks | | `fb-certs.s3.amazonaws.com` | AWS | | `fb-cosigner-images.s3.amazonaws.com` | AWS | | `fb-customers.s3.amazonaws.com` | AWS | | `fb-customers.s3.amazonaws.com/uploads` | AWS | | `download.docker.com` | Docker | | `github.com` | GitHub | | `cdn.registry.gitlab-static.net` | GitLab | | `gitlab.com` | GitLab | | `registry.gitlab.com` | GitLab | | `download.01.org` | Intel SGX Driver | | `bootstrap.pypa.io` | Python Software Foundation | | `files.pythonhosted.org` | Python Software Foundation | | `pypi.org` | Python Software Foundation | | `pypi.python.org` | Python Software Foundation | Fireblocks-owned domains differ based on the specific Fireblocks SaaS environment you are connected to. If you are connected to the European or Swiss SaaS, update your allowlist according to the domains listed in the table below: | Fireblocks SaaS | Domains to Allow | | - | - | | Global | `mobile-api.fireblocks.io` `signurl.fireblocks.io` `s3signurl.fireblocks.io` | | Europe | `eu2-mobile-api.fireblocks.io` `eu2-signurl.fireblocks.io` `eu2-s3signurl.fireblocks.io` | | Swiss | `eu-mobile-api.fireblocks.io` `eu-signurl.fireblocks.io` `eu-s3signurl.fireblocks.io` | Additionally, ensure port access is configured for the following services: | Port | Service URL | | - | - | | 443 | `https://mobile-api.fireblocks.io` | | 443 | `https://s3signurl.fireblocks.io` | | 443 | `https://fb-certs.s3.amazonaws.com` | | 443 | `https://fb-customers.s3.amazonaws.com/uploads/` | | 443 | `https://bootstrap.pypa.io/get-pip.py` | | 443 | `https://download.docker.com/linux` | | 443 | `https://download.01.org/intel-sgx/` | | 5000 | `https://registry.gitlab.com/customer-cosigner` | > [!NOTE] > > If you cannot support DNS IP whitelisting with an advanced firewall or within the cloud itself, you must allow egress traffic to any destination on port 443. ### Step 1.2: Set up and configure an on-prem SGX server Your on-prem SGX server should meet the following hardware requirements: * **CPU processor:** Only one of the following [Intel® processors](https://www.intel.com/content/www/us/en/architecture-and-technology/software-guard-extensions-processors.html) * **CPU cores:** Minimum 4 * **OS:** Ubuntu 22.04 LTS, 24.04 LTS, or 26.04 LTS (Canonical) with - * Latest Linux kernel version * Latest Intel microcode (BIOS update) * Ubuntu 26.04 LTS requires API Co-signer version 2026.10.21 or later * **RAM:** Minimum 16GB * **Storage:** Minimum 128GB * **SGX Memory:** Minimum 2GB EPC > [!NOTE] > > Fireblocks also supports on-prem SGX servers installed on OVHcloud providers. Additionally, configure your on-prem SGX server BIOS to match the following setup: * Enable Intel SGX (Software Guard Extension) * Enable DCAP (FLC) * Disable hyper threading * Disable Intel SpeedStep Technology * Disable Onboard VGA ### Step 1.3: Verify SGX is enabled on your SGX server After setting up your on-prem SGX server, confirm that SGX is enabled. The SGX Co-signer requires a server with SGX enabled and the latest patches applied. This verification ensures smooth operation and avoids potential issues. To verify that SGX is enabled on the VM, run the following commands with root privileges: ```bash theme={"system"} apt update apt upgrade apt install cpuid cpuid -1 | grep -i sgx ``` Verify the following: 1. `SGX`: Software Guard Extensions supported is **true** 2. `SGX_LC`: SGX launch config supported is **true** ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/fe23ee1939feee21b46aa47061c3d01ab28840989133235e3d3b578d3722630a-Screenshot_2024-12-06_133814.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=79db17bab8bfbeff948a5374cf9a8363) ### Step 1.4: Install the SGX drivers 1. [Subscribe to the Intel PCS for ECDSA Attestation and to obtain the required API keys](https://api.portal.trustedservices.intel.com/provisioning-certification). 2. Set up Intel's reference caching service, the Provisioning Certification Caching Service (PCCS): 1. **Redhat/Debian:** [Installation Guide, page 39](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_SW_Installation_Guide_for_Linux.pdf?#page=39) 2. **Another Debian approach:** Follow the steps in the document above (intel-dcap.pdf), but skip the *Verify the empty cache* step. 3. **Cache Fill Mode:** [Intel SGX DCAP Caching Service Design Guide, page 38](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf#page=38) 3. Provision the Intel SGX-enabled platform for Intel SGX workloads. The goal is to run PCKIDRetrievalTool. *Make sure pckid\_retrieval.csv is created.* This file will be used to register the CPU data with Intel. Without this registration, the remote attestation will fail. 1. **Debian resources:** [Intel SGX DCAP Quick Install Guide, page 4](https://drive.google.com/file/d/1lZWM4_dTSdOID9PdKGFDenkyi4DA9OR8/view) 2. **Redhat resources:** Download the binary file from one of these locations: 1. [https://download.01.org/intel-sgx/sgx-dcap/1.18/linux/distro/rhel8.6-server/](https://download.01.org/intel-sgx/sgx-dcap/1.18/linux/distro/rhel8.6-server/) 2. [https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/main/tools/PCKRetrievalTool](https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/main/tools/PCKRetrievalTool) 4. Convert the pckid\_retrieval.csv file to JSON format using this Python script and submit it to Intel for registration. You can view a sample JSON [here](https://drive.google.com/file/d/1x9NCvFHYMR8-MaauEWspZZAsZjU6qjdS/view). ```python theme={"system"} import json import sys if len(sys.argv) != 2 and len(sys.argv) != 3: print("usage: pckid2json ") exit(0) f = open(sys.argv[1], 'r') data = f.read().split(',') f.close() if len(data) == 5: print("WARNING: platform metadata is missing from pckid file") elif len(data) != 6: print("ERROR: pckid file has invalid format") exit(-1) obj = {} obj['enc_ppid'] = data[0] obj['pce_id'] = data[1] obj['cpu_svn'] = data[2] obj['pce_svn'] = data[3] obj['qe_id'] = data[4] if len(data) == 6: obj['platform_manifest'] = data[5] if len(sys.argv) == 3: f = open(sys.argv[2], 'w') json.dump(obj, f) f.close() else: print(json.dumps(obj)) ``` 1. For more information about the registration process, visit the following references: 1. [Design Guide for Intel SGX PCCS, page 20](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf#page=20\&zoom=100,92,200) 2. [Get PCK Certificates, V4](https://api.portal.trustedservices.intel.com/content/documentation.html#pcs-certificates-v4) 5. When prompted to verify the provisioning data, skip this step. 6. Reimage the Intel SGX-enabled platform. 7. Load the Intel SGX runtime stack onto the reimaged system: 1. **Debian:** Refer to the [Intel SGX DCAP Quick Install Guide, page 5](https://drive.google.com/file/d/1lZWM4_dTSdOID9PdKGFDenkyi4DA9OR8/view). Note that steps 5 and 6 are bulked up in the "Runtime configuration" section. 2. **Redhat:** Refer to the [Intel Confidential Computing Documentation](https://cc-enabling.trustedservices.intel.com/intel-sgx-sw-installation-guide-linux/01/introduction/). ### Step 1.5: Registration with Intel 1. Follow the registration instructions located [here](https://api.portal.trustedservices.intel.com/content/documentation.html#pcs-tcb-info-v4). Note that you'll need the JSON format of the pckid\_retrieval.csv file for this step. 2. Verify that your CSV is registered with Intel via Fireblocks: ```bash theme={"system"} curl -L "https://mobile-api.fireblocks.io/sgx/certification/v3/pckcert?qeid=FC88E54A32E9BE6455A01CE78A55B85C&encrypted_ppid=635DBA646F13077DC75291B12ABE96D4D69A956D7FF5524D8090290EEB8ACC6450F72CD7539D1578E636911321D96E0CEC15E04FACBC7652DE1775F76DF460D472A7F5C8118B22C37F4590789F46161D99F2758D120C5BD4CD8FD302C444F56E620A98DFAFD156887B73DAEEB04EA6C9007C2EEB9B00DF49FBBD4B9535B8DB61445A28F73CADDADB36B27E35DACE6A4287621D2FF6E01B31C579FC9D73512B631558427370020DAB10DF315D740400ED73D64AACBFC76A0B88975A52086CD656809A58821A9B8516A4571BF33CBA042B76EC4409745635ADF2DE5F62FA8C0F6E4970712209F3D2EF83E5850646105B841F1905CB3E7F5AE21BF8759250C9981F8DEA229265CC619D9990842AC71739BCF155FCBA6AD262EDF04AA8A9E668E2F87A3FA822E7F6F330CA955C9B6244E5C35BFA71DA08655C1244156E598F97B97BAF1DF6CF79B624E5129AF190A24930436BDCC2371BEA322EEF2D733769372D5F763763B937BF680D4245441F234623051FAFCF0DA8FA79D744E3F261FD352275&cpusvn=0C0C0F0EFFFF01000000000000000000&pcesvn=0C00&pceid=0000" ``` 3. A certificate will appear in the response if the registration was successful: ```yaml theme={"system"} -----BEGIN CERTIFICATE----- MIIE8jCCBJigAwIBAgIUUWcKdCIgJwOmz/lT0UrRd9zVUU8wCgYIKoZIzj0EAwIw cDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBDSyBQbGF0Zm9ybSBDQTEaMBgGA1UECgwR SW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQI DAJDQTELMAkGA1UEBhMCVVMwHhcNMjQxMjAzMDg1MzA2WhcNMzExMjAzMDg1MzA2 ……. ……. ……. -----END CERTIFICATE----- ``` ## Step 2: Add a Co-signer to the workspace using an API user Follow the instructions to [get the pairing token](/cosigners/overview/api-cosigner-installation-flow#get-the-pairing-token). Ensure you copy to your clipboard the following items, which you will use during the installation process: * The API user's pairing token * The download link of the Co-signer's installation script ## Step 3: Install and connect the Co-signer to the workspace > [!NOTE] > > You must have root privileges on the Co-signer server to install the Co-signer > > Ensure you are logged in as a root user or use `sudo` to execute the commands. ### Step 3.1: Download the installation script Using the download link of the SGX Co-signer installation script you copied from the Console, run the `curl` command to download the package directly to your machine. You can find the download link in the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). Paste the appropriate URL into the following command: ```bash theme={"system"} curl -o cosigner "URL" ``` ### Step 3.2: Run the installation script After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` To install the Co-signer, run: ```bash theme={"system"} ./cosigner setup ``` You will be prompted to enter the **Pairing token** for the API user, which you retrieve from the Fireblocks Console. This token pairs the API user with the Co-signer. At this stage, you will have the option to configure the Callback Handler parameters for the API user connecting the Co-signer to the workspace. This feature is optional. You can [configure it later through the Console, APIs, or locally](/cosigners/operations/api-cosigner-operate) from the Co-signer's host machine. For detailed instructions on setting up your Callback Handler's interface to the Co-signer and implementing its logic and code, refer to the [Setup API Co-signer Callback Handler](/reference/api-cosigner-setup-callback-handler) section. The setup process validates the machine's hardware and installs the necessary drivers to support the appropriate SGX version and the SGX Co-signer's executable image. It includes an attestation flow to ensure that the SGX Co-signer's executable runs securely inside an Intel SGX enclave. Additionally, the script installs other required components. Once the installation is complete, the Co-signer will automatically start running. At the end of the process, the Co-Signer generates a JSON configuration file, which can be used for future configuration updates. ### Step 3.3: Approve MPC key shares for the API user If the API user used to pair with the Co-Signer and connect it to your workspace has an Admin or Signer role, the workspace owner will receive a notification. This notification prompts them to approve a new MPC key share request for that API user in the Fireblocks mobile app. You can now see the Co-signer you installed in the Co-signers tab within the Console's Developer Center. Observe it is online and that the API user is paired to it. > [!NOTE] > > To check the Co-signer's status and observe the logs, see the SGX Co-signer Maintenance article. # Intel SGX Co-signer Architecture Source: https://developers.fireblocks.com/cosigners/on-premises/sgx-architecture-onprem > [!NOTE] > > Learn how to install SGX Co-signer on-premises in the [following guide](/cosigners/on-premises/install-api-cosigner-onprem). ## Resources used by the SGX Co-signer The Fireblocks SGX Co-signer utilizes Intel's SGX enclave and attestation mechanisms. It can be deployed either in cloud service providers that support compatible SGX servers or on-premise using a bare-metal server. Also, an SGX driver must be loaded into the machine. This is the only resource that is required to install and operate the Co-signer on the host machine. The Co-signer's database is encrypted and stored on the host machine's disk, serving as the Co-signer's persistent storage. > [!WARNING] > > **Important**: Allocate a separate machine for each Co-signer to prevent conflicts and ensure isolation, enhancing security. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/1ac3082630bdf2681904ab3d00f491f79bd5f8ef4025df3d6c3958eb9342566f-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=e03c764718b4c83de4c47a4d3cbe1bdd) ## Secure Co-signer database encryption scheme The following process is implemented to build and secure the Co-signer's database: 1. The Co-signer, upon initialization, connects to the [Intel-SGX Remote Attestation server](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/attestation-services.html) that belongs to the Fireblocks SaaS and retrieves the key that encrypts its database: `FBKS-DB-Key` 2. The Co-signer initializes its encrypted database using the key `FBKS-DB-KEY` 3. The Co-signer encrypts `FBKS-DB-KEY` using the SGX hardware key of the host machine. 4. The Co-signer saves its encrypted database and the encrypted form of `FBKS-DB-KEY` in the host machine, serving as its persistent storage 5. The Co-signer cannot operate unless it is attested against Fireblocks' Remote Attestation server. This is illustrated in the block diagram below: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/a3cd183923542641d62c25dfe6000bc85c4c2db7a2151bdbb51ef19b31dda23b-image.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=a0bbb047d06aa91ed17bdcd7c70afafb) ## Installation artifacts The SGX Co-signer is comprised of several components: 1. Installation script: This is the CLI interface for installing and managing the Co-signer. You can retrieve the installation script from the Console under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). The script includes the path to a Docker image registry for downloading the Co-signer Docker image. It supports downloading images of various versions, with default versions specified if none are explicitly provided. See the [SGX Co-signer version history](/cosigners/sgx/api-cosigner-versions-sgx) for details on the image version. 2. Docker image: The Co-Signer's executable is wrapped in a Docker image to provide safe, versioned, and consistent installation across different platforms. The Docker image provides all the libraries and dependencies of the main executable. 3. Main loading executable: A slim binary that downloads and verifies the enclave and exposes command line interfaces. 4. Secure SGX enclave: This component contains most of the Co-Signer's code and logic. It is both encrypted and signed by Fireblocks, and it can be downloaded from the Fireblocks server during setup. * **Q: Should I verify the executable's signature?**\ If you are using the correct Docker image, as specified by the script, there is no need for any other verification, as it is embedded in the image. * **Q: Why is the enclave encrypted?**\ SGX allows for saving the enclave encrypted on disk with a hardware key only available to the specific CPU where the enclave would be executed. Secured enclaves prevent the risk of modification, make it impossible to reverse engineer, and hide any potential secrets embedded within them. This enclave alone is capable of reading the secrets stored by the Co-Signer. * **Q: Should I verify the enclave's signature?**\ There is no need for that. The Fireblocks signing key is embedded within the loading executable, which verifies the enclave's signature whenever it is loaded. * **Q: Where do I find the enclave in the docker image?**\ The enclave is only downloaded when the Co-signer goes through a setup or upgrade process, so it is omitted from the Docker image. The name of the file is `enclave.signed.so`. The chain of trust follows the sequence: **script > image > executable > enclave**, with Fireblocks ensuring compatibility between these components. During setup or upgrade, the Co-signer downloads the latest enclave version from Fireblocks servers, which is guaranteed to be compatible with the installed executable. To ensure the enclave uses the latest published version, always use the most recent Co-signer Docker image. The safest approach is to install the Co-signer using the latest installation script. # SGX API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/on-premises/sgx-maintenance-onprem > [!NOTE] > > You must have root privileges on the Co-signer machine to perform maintenance operations. Ensure you are logged in as a root user or use `sudo` to execute the commands. ## View the logs You can export the logs to a file in the local directory, tagged with the current date and time, by running the following command: ```bash theme={"system"} ./cosigner logs ``` Append a number to the command to retrieve the specified most recent amount of lines. By default, the Co-signer uses the Docker logging container. However, for better monitoring, the logs can be streamed to an external log delivery service, such as ELK, Splunk, or Datadog. The SGX Co-signer generates two different types of logs for troubleshooting and auditing: * `run.log` is an automatically generated log containing all of your Co-signer setup and configuration history. * `customer_cosigner.log` contains workspace operation data, such as the workspace's transaction approving and signing history and new connection approvals. These log files are separated into 10 files limited to 4.1MB each. They are automatically overwritten in chronological order. Your API Co-signer logs are structured using the following Regular Expression (RegEx): ```bash theme={"system"} ${AppName}:${Line} ${LogLevel} ${dd/MM/yyyy} ${HH:mm:ss,fff} ${ClassName} ::${MethodName} - ${LogMessage} ``` These logs are collected by the Docker container’s `STDOUT` stream and written into a log file on the container’s host machine at the location specified below. These log files are rotated with a max file size of 4MB for each log file. ```text theme={"system"} /databases/cosigner/log/customer_cosigner.log /databases/cosigner/log/customer_cosigner.log.1 /databases/cosigner/log/customer_cosigner.log.2 ... ``` > [!NOTE] > > The Co-signer is Docker-based and uses the Docker JSON file driver for logging. By default, the driver uses a standard logging output but can be configured to support other logging mechanisms. Refer to the official Docker documentation for more detailed information. *** ## Verify SGX is enabled on the machine After completing your server creation or installation, verify that SGX is enabled on the server. The Fireblocks API Co-Signers can only run on SGX-enabled servers with the latest patches, and verifying your configuration helps ensure you do not run into any potential issues later on. 1. Run the following commands on the server: ```bash theme={"system"} sudo apt update sudo apt upgrade sudo apt install cpuid cpuid -1 | grep -i sgx ``` 2. Verify the following: 1. SGX: Software Guard Extensions supported is `true`. 2. SGX\_LC: SGX launch config supported is `true`. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/0c0a9cb9db9029ad2559a58ddcde807472c7674dc0a81d25cd6dd58f57f37c5b-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=128b5d0f685e48863e18cc368d7d140b) > [!NOTE] > > Azure Standard\_DC\*\_v2 instances do not support SGX2, but it is not necessary to run the signer. *** ## List the paired API users You can list all API users paired with the Co-signer across the connected workspaces by running the following command: ```bash theme={"system"} ./cosigner list-users ``` The output will display a list of all API users paired with your Co-Signer, including the workspace name they are connected to, the API user's ID (its API key), and the associated Callback Handler server URL (if applicable). *** ## Retrieve the public key You can retrieve the Co-signer's public key, used by your optional Callback Handler server to authenticate requests from the Co-signer, by running the following command: ```bash theme={"system"} ./cosigner print-public-key ``` *** ## Stop the Co-signer You can stop the Co-signer by running the following command: ```bash theme={"system"} ./cosigner stop ``` *** ## Start the Co-signer You can Start the Co-signer by running the following command: ```bash theme={"system"} ./cosigner start ``` *** ## Restart the Co-Signer You can restart the Co-signer by running the following command: ```bash theme={"system"} ./cosigner restart ``` *** ## Retrieve the running image version You can retrieve your running Co-Signer version by running the following command: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image' + " " + '.[0].Image' ``` *** ## Retrieve the script version You can retrieve your current Co-Signer script version by running the following command: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` *** ## Update the Co-signer In the Fireblocks Console, go to [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer) and copy the download link of the Intel SGX installation script. The link is valid for seven days. Use the `curl` command to download the script to the path where the existing Co-Signer script is present. Paste the copied URL into the following command: ```bash theme={"system"} curl -o cosigner "URL Download Link" ``` > [!NOTE] > > If you have any issues with finding the installation script URL, please contact [Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=360003372200\&tf_360023089139=global_settings\&tf_360023089159=get_api_co-signer_installation_script). After downloading the installation script, navigate to the directory containing the script and modify the script's permissions to make it executable: ```bash theme={"system"} chmod +x cosigner ``` Stop the Co-signer container by running the following command: ```bash theme={"system"} ./cosigner stop ``` Run `docker ps -a` and check the output. There should be no Co-signer container running. Backup the Co-signer's database by running the following command: ```bash theme={"system"} mv /databases/cosigner/enclave/enclave.signed.so /databases/cosigner/enclave/enclave.signed.so_old ``` Remove the current revision file: ```bash theme={"system"} sudo rm -f .revision ``` You can now update the Co-signer to the latest version by running the following command: ```bash theme={"system"} ./cosigner start ``` You can update to a specific image version by replacing the `` : ```bash theme={"system"} ./cosigner start ``` Run `docker ps -a` and check the output. You should see the Co-signer container running. If the update process did not succeed, try re-installing the Co-signer using the same version, but first remove the current version of the Co-Signer by running the following commands: ```bash theme={"system"} sudo rm -rf /databases sudo rm -f .local_config .revision ``` and then run: ```bash theme={"system"} ./cosigner setup ``` If that still doesn't work, refer to [API Co-signer Troubleshooting](/cosigners/operations/api-cosigner-troubleshooting). *** ## Migrate to a new machine Begin by setting up a new SGX-capable machine in your cloud or on-premises environment. Migrating the SGX Co-Signer to a new machine is most effectively performed using backup data from the existing machine. This approach minimizes downtime and reduces the risk of interruptions to your workspace operations. The following files and folders are generated automatically during the SGX Co-signer setup and are essential for backup and recovery purposes: 1. `/databases/cosigner/db`: The Co-signer DB folder stores the `secrets.db` file, the Co-Signer's secrets database. This file is encrypted with a key generated in a secure SGX enclave. 2. `/databases/cosigner/backup`: The backup folder maintained by the Co-Signer stores the backup upon any change to the database. The files are formatted using the following timestamp: `%Y%m%d%H%M%S.db` (e.g. `20220710184350.db`) based on the backup's date. 3. `/databases/cosigner/enclave/ra_loader_enclave.signed.so` - The enclave loader. Backup the files `secrets.db` and `ra_loader_enclave.signed.so` and copy them to the same folder locations on the new server: * `/databases/cosigner/db/secrets.db` * `/databases/cosigner/enclave/ra_loader_enclave.signed.so` After the above files have been copied to the new server, run `./cosigner start`. If you don't have backups of either `secrets.db` or `ra_loader_enclave.signed.so` files, unpair (re-enroll) the API users that are associated with the Co-signer and set up a new Co-signer on the new machine. Install the replacement on [Azure](/cosigners/azure/install-api-cosigner-azure), [Azure Marketplace](/cosigners/azure/install-api-cosigner-azure-marketplace), [IBM Cloud](/cosigners/ibm/install-api-cosigner-ibm), [Alibaba Cloud](/cosigners/alibaba/install-api-cosigner-alibaba), or [on-premises](/cosigners/on-premises/install-api-cosigner-onprem). # SGX API Co-signer Version History Source: https://developers.fireblocks.com/cosigners/on-premises/sgx-versions-onprem Fireblocks updates the API Co-signer software to include stability enhancements and new features. This article provides details about the most recent SGX Co-signer versions. Always use the latest version listed here for optimal performance. ## Latest SGX Co-signer image version ### **Version 2026.10.21 (October 2026)** **Version hash:** `90324e06` * Callback enhancement: the number of change outputs is now included in the transaction metadata sent to the callback handler * Base image and root CA updates * Security fixes and updates * Support RHEL 9 and Ubuntu 26.04 hosts *** ## SGX Co-signer image version history ### **Version 2026.10.03 (October 2026)** **Version hash:** `77774ae4` * Security and bug fixes ### **Version 2026.09.16 (September 2026)** **Version hash:** `0449f81f` * Fast signing for EdDSA * Base image updated * Security updates ### **Version 2026.08.19 (August 2026)** **Version hash:** `f5ab4d7f80` * Callback responses may now be signed with ECDSA P-256 (ES256) in addition to RSA (RS256) * Security fixes ### **Version 2026.07.20 (July 2026)** **Version hash:** `86d1be9f9b` * Source and destination tags are now sent to the callback handler * Security improvements and bug fixes ### **Version 2026.06.24 (June 2026)** **Version hash:** `80e34d008a` * Performance improvements and bug fixes ### **Version 2026.05.27 (May 2026)** **Version hash:** `1b31e6b7fb` * Support for Fireblocks BCM * Support for batch attachment of tags * Performance improvements and bug fixes ### **Version 2026.04.27 (April 2026)** **Version hash:** `1d5b48eeef` * Performance improvements, security enhancements and bug fixes ### Version 2026.03.02 (March 2026) **Version hash:** 95a317d3c7 * Added support for POLICY\_CHANGE approval request type (the customer co-signer can now approve policy changes via Policy Service v2) * Performance improvements and bug fixes ### Version 2026.01.29 (January 2026) **Version hash:** 340d9f6057 * Bug fixes and performance improvements ### Version 2025.12.30 (December 2025) **Version hash:** a294449c1b * Added support for "Protected Tags" approval requests ### Version 2025.12.11 (December 2025) **Version hash:** c44604398e * The callback handler can now authenticate using a certificate signed by a root CA certificate preloaded to the Co-signer * The callback handler can now authenticate using both JWT and a certificate provided directly to the Co-signer (or signed by a root CA certificate provided to the Co-signer) * The callback can now return a "retry" response on exchange withdraw operation ### Version 2025.10.09 (October 2025) **Version hash:** 4e34888c01 * Fixed bug parsing messages after websocket reconnect. * Fixed bug sending messages on half-closed websocket connections. ### Version 2025.08.12 (August 2025; US environments only) **Version hash:** 19c07421dc * Fixed WebSocket bug causing performance issues ### Version 2025.07.16 (July 2025) **Version hash:** bb61e507 * Fixed bugs and increased performance * Better support for web sockets ### Version 3.10.1 (April 2025) **Version hash:** 7160d7b833 * Bug fixes and performance improvements ### Version 3.7.1 (October 2024) **Version hash:** 4ec196aec5 * Decreased signing latency, along with general performance increases * Remote management capabilities through Fireblocks Console * Support for more types of approval requests * Various fixes and improved handling of internal errors * Timing details are logged after every outgoing network request * The cosigner version will be reflected in the HTTP User-Agent header ### Version 3.6.5 (February 2024) **Version hash:** 3b807e2af7 * Improved logging for easier problem-solving * Improved stability, security, and performance * Enhanced deployment flexibility * Added support for the Callback Handler retry mechanism, enabling the Callback Handler to respond asynchronously * Updated major and minor dependencies * Preliminary support for upcoming features * Bug fixes ### Version 3.5.0 (August 2023) * [MPC-CMP security enhancements](https://support.fireblocks.io/hc/en-us/articles/10167020461596-MPC-CMP-security-enhancements-version-update-August-2023) * Bug fixes ### Version 3.3.0 (January 2022) * Added support for HTTP network proxy on outbound requests * Bug fixes *** ## Latest SGX Co-signer script version ### Version 2025.10.11 (December 2025) * New default Docker image: 2025.10.11 ## SGX Co-Signer script version history ### Version 2025.10.09 (October 2025) * New default Docker image: 2025.10.09 * Switched to Calendar Versioning to align script versions with default image versions ### Version 1.1.13 (August 2025) * New default Docker image 2025.08.12 ### Version 1.1.11 (April 2025) * New default Docker image 3.10.1 ### Version 1.1.10 (January 2025) * New default Docker image 3.7.1 ### Version 1.1.9 (February 2024) * New default Docker image 3.6.5 ### Version 1.1.8 (January 2024) * Added support for Callback Handler configuration via Azure Marketplace * Same default Docker image as in image version 3.5.0 and used as in script version 1.1.7 ### Version 1.1.7 (August 2023) * New default Docker image 3.5.0 ### Version 1.1.6 (February 2023) * Added support for Docker version 1.29.2 * Added validation for microcode: * Additional information about the microcode version appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem * Added validation to make sure Hyper-Threading is disabled * If Hyper-Threading is enabled, additional information appears in the logs * Relevant only to customers with SGX Co-signers installed on-prem ### Version 1.1.5 (February 2022) * New default Docker image 3.3.0 * Added support for Co-signers in high-availability * Updated authentication model * Added support for HTTP network proxy on outbound requests *** ## Checking SGX Co-signer versions ### How do I check the version of my running SGX Co-signer image? To check your running SGX Co-signer image version, run the following command on your VM: ```bash theme={"system"} docker inspect cosigner | jq -r '.[0].Config.Image +" " + . [0].Image' ``` ### How do I check the version of my running SGX Co-signer script? To check your running SGX Co-signer script version, run the following command on your VM: ```bash theme={"system"} head cosigner -n 3 | grep -i ver ``` # API Co-signer Maintenance Source: https://developers.fireblocks.com/cosigners/operations/api-cosigner-maintenance This article lists the maintenance operations available for an API Co-signer. The steps differ by platform, so follow the Maintenance page for your Co-signer type. > [!NOTE] > > Due to the enclave architecture of the Google Cloud Confidential Space Co-signer, maintenance operations can only be performed through Google Cloud's portal or using `gcloud`. Co-signer maintenance include: * View the logs * Observe the status * List the paired API users * Retrieve the public key (used for the Callback Handler JWT authentication) * Stop the Co-signer * Restart the Co-signer * Retrieve the running version * Update the Co-signer * Migrate to a new machine * Configure a proxy server * Configure the communication protocol Use the [Co-signer management tab](https://support.fireblocks.io/hc/en-us/articles/17923671680540-The-Co-signer-management-tab) to observe the Co-signer's online or offline status and the list of paired API users. Logs, restarts, upgrades, and the other operations above are on each platform's Maintenance page. ## Check Co-signer status via API To check whether a Co-signer is online or offline programmatically, call [Get API key](/api-reference/cosigners-beta/get-api-key) for an API key paired with the Co-signer. Infer the Co-signer's status from the `lastSeen` timestamp in the response — a recent value indicates the Co-signer is online. To find the `cosignerId` and paired `apiKeyId`, use [Get all cosigners](/api-reference/cosigners-beta/get-all-cosigners) and [Get all API keys](/api-reference/cosigners-beta/get-all-api-keys). # Operating the API Co-signer Source: https://developers.fireblocks.com/cosigners/operations/api-cosigner-operate Once the Co-signer is connected to your workspace, you can manage it through the Console or APIs. This includes sending commands such as pairing additional API users or configuring the Callback Handler for a paired API user. For all types of SGX Co-signers and the AWS Nitro Co-signer, all operations can also be performed via the command-line interface (CLI) on the Co-signer's host machine. However, due to the enclave architecture of the Google Cloud Confidential Space Co-signer, all operations on this type of Co-signer must be performed exclusively through the Console or APIs. A Google Cloud Confidential Space Co-signer can be connected to only one workspace, and configuration stays in that workspace. ## Co-signer API operations Refer to [Fireblocks Cosigners API](/reference/getcosigners) for the list of Co-signer operations you can do using Fireblocks APIs. The operations include: * Get all the Co-signers that are connected to the workspace * Get the details of a specific Co-signer that is connected to the workspace * Rename the Co-signer * Get a list of all the API users that are paired with the Co-signer * Get the details of a specific API user that is paired with the Co-signer * Add a Co-signer to the workspace * ❗ Pair an additional API user with a Co-signer * Unpair an API user from a Co-signer * ❗Update the callback handler of a paired API user > [!NOTE] > > **The operations that are marked with ❗ will only work with Co-signers with the following versions:** > > * SGX Co-signer: version 3.7.1 or later > * AWS Nitro Co-signer: version 2.0.5 or later > * Google Cloud Confidential Space Co-signer: version 0.9.4 or later ### Track pending Callback Handler and pairing changes Pairing an API user, unpairing an API user, and updating a Callback Handler are asynchronous commands. Fireblocks accepts the [Update API key callback handler](/api-reference/cosigners-beta/update-api-key-callback-handler) request with HTTP `202` and returns a `Location` header for the new request. The change takes effect only after the request completes. To check its progress, call [Get request status](/api-reference/cosigners-beta/get-request-status). The response includes the command `type` (`PAIR_API_KEY`, `UPDATE_CALLBACK_HANDLER`, or `UNPAIR_API_KEY`) and its `status`: * `WAITING_FOR_APPROVAL`: The change is waiting for approval. * `APPROVED`: The change was approved. * `COMPLETED`: The change was applied. * `REJECTED` or `CANCELLED`: The change was not applied. If you receive an error saying a change to the Callback Handler is already pending, an earlier change for that API user hasn't finished yet. Wait until the earlier request reaches a final status, then submit your new change. *** ## Co-signer Console operations You manage paired API users and their Callback Handlers from the **Co-signers** tab in the Console. ### Configure the Callback Handler 1. In the Console, go to **Developer center** > **Co-signers**. 2. Expand the Co-signer. In the **Callback Handler** column, select **+**. 3. Select **Configure Callback Handler**. 4. Enter the Callback Handler URL and authentication parameters. See [Setup API Co-signer Callback Handler](/cosigners/callback-handler/create-api-co-signer-callback-handler). Select **Update**. > [!NOTE] > > Configuring the Callback Handler from the Console requires a Co-signer version that supports platform commands. See the version list in [Co-signer API operations](#co-signer-api-operations). > [!WARNING] > > The Console supports only Option 1 (public key authentication) and Option 2 (certificate pinning) from [Securing communication](/cosigners/callback-handler/cosigner-callbackhandler-secure-communication-authentication). Options 3, 4, and 5 (Root-CA and hybrid options) are currently supported only through [local configuration](#co-signer-command-line-operations) with the Co-signer scripts. For other Co-signer operations in the Console, such as renaming a Co-signer or pairing an API user, see the [Co-signer management tab](https://support.fireblocks.io/hc/en-us/articles/17923671680540-The-Co-signer-management-tab) in the Help Center. *** ## Co-signer command-line operations This section covers operations related to Co-signer management, including adding and configuring API users. For maintenance operations such as viewing logs, updating the Co-signer, and more, refer to the [API Co-signer maintenance](/cosigners/operations/api-cosigner-maintenance) article. ### List the paired API users You can list all API users paired with the Co-signer across the connected workspaces by running the following command: ```bash theme={"system"} ./cosigner list-users ``` The output will display a list of all API users paired with your Co-Signer, including the workspace name they are connected to, the API user's ID (its API key), and the associated Callback Handler server URL (if applicable). ### Add API user You can pair an additional API user with your Co-Signer instance by running the following command: ```bash theme={"system"} ./cosigner add-user ``` > [!NOTE] > > This command works only after the first API user has been paired during the Co-Signer installation. You will be prompted to provide the API user's pairing token, which can be retrieved from the Console. If you opt to connect this API user with a Callback Handler, you must also provide the Callback Handler's URL and its public key or certificate. For more information, refer to the **Setup API Co-Signer Callback Handler** article. ### Setup and update Callback Handler for API user To set up or update the Callback Handler of an API user paired with your Co-signer, run: ```bash theme={"system"} ./cosigner callback-update [API user ID] ``` If only one API user is paired with your Co-signer, you don't have to specify the `API user ID`, which is its API key. You're prompted for the Callback Handler's URL and authentication method, then for the public key, certificate, or root CA certificate the method requires. The root CA certificate and hybrid authentication methods (Options 3, 4, and 5 in [Securing communication](/cosigners/callback-handler/cosigner-callbackhandler-secure-communication-authentication)) are available in SGX Co-signer version 2025.12.11 and later and AWS Nitro Co-signer version 2026.08.19 and later. Earlier versions offer only public key and certificate authentication. The command applies each value as follows: | Setting | What `callback-update` does | | - | - | | Callback Handler URL | Always updates it. An existing URL can't be cleared. | | Public key (JWT authentication) | Sets it only if the API user has no public key configured. An existing public key is kept. | | Root CA certificate | Sets it only if the API user has no root CA certificate configured. An existing root CA certificate is kept. | | Pinned certificate | Sets it only if the API user has no Callback Handler URL and no pinned certificate configured. An existing pinned certificate is kept. | A root CA certificate and a pinned certificate can't be used together. Setting a root CA certificate removes the pinned certificate, and setting a pinned certificate removes the root CA certificate. > [!WARNING] > > The command doesn't show an error when it keeps an existing value. If you enter a new public key, certificate, or root CA certificate for an API user that already has one, the command completes, but the Co-signer keeps the existing value. To change a public key or pinned certificate that is already configured, use the APIs or the Console. The APIs and the Console don't accept a root CA certificate, so to change an existing root CA certificate, unpair (re-enroll) the API user and pair it again with the Co-signer using the `add-user` command. Re-pairing also works for any of the other settings. If only one API user is paired with your Co-signer, unpairing it results in a full Co-signer setup. > [!NOTE] > > To configure the Callback Handler's public key or certificate using APIs or through the Console, your Co-signer should meet the requirements for Co-signer platform commands, as described in the [Co-signer API operations section](/cosigners/operations/api-cosigner-operate#co-signer-api-operations) within this article. # API Co-signer Troubleshooting Source: https://developers.fireblocks.com/cosigners/operations/api-cosigner-troubleshooting ## Contacting Fireblocks API Co-signer Support If you have a question or need assistance, [contact Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new) and include the following information in your request: * Your workspace name * The first four characters of the API key associated with the API user used to connect the Co-signer to the workspace. * Indicate whether the API user is paired with an active Co-signer or is in the process of completing the initial setup. Copy the logs from the API Co-signer and include them in the support ticket. Refer to [the Co-signer maintenance documentation](/cosigners/operations/api-cosigner-maintenance) to locate the logs for your specific type of Co-signer. *** ## Error codes | Error Code | Description | Possible Cause | | - | - | - | | 401 | Unauthorized | User account-related issue. | | 403 | Forbidden | Your firewall, proxy, or WAF is blocking communication. | | 500 / 502 / 504 | Server error | Temporary server-side issue. | *** ## Error messages ### Error message "MPC setup not completed" This error message indicates that the workspace Owner must still approve the API user's MPC key shares. #### Resolution Check the Owner's device for pending MPC key share approval notifications. * If the notification is less than 12 hours old, the Owner can still approve the key shares, and the API user will be ready to sign transactions. Once the owner approves your new MPC keys on their Fireblocks mobile app. You have five days (120 hours) to complete the MPC registration. * If the Owner is unable to approve the request due to [Error 52](https://support.fireblocks.io/hc/en-us/articles/9108149928092-Error-52), deny the pending request and [submit a request to Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new). * If there is no notification or another error, unpair the API user and then pair it with the Co-signer again to resend the approval request. ### API Co-signer does not sign transactions The API Co-signer may not sign transactions if its transaction queue is full. In this case, the API logs will include an error stating that the "**queue is full**". This means the API Co-signer's internal queue is overloaded and must be restarted. This is separate from your workspace transaction queue. #### Resolution To resolve a full Co-signer queue: 1. Cancel all the pending transactions. 2. Make sure there are no pending requests on the Co-signer. 3. Restart the Co-signer. 4. Wait a few minutes to confirm that the queue is empty. 5. Submit a new transaction. 6. Confirm the transaction is signed by the Co-signer as expected. > [!NOTE] > > **Confirm your Co-signer meets minimum hardware requirements** > > The root cause of the full queue may be server CPU capabilities. Confirm that your Co-signer server meets the recommended hardware requirements. ### API Co-signer does not sign raw-signing transactions When this issue occurs, the API Co-Signer signs some raw signing transactions but not others. This usually results from [signature caching for off-chain messages](https://support.fireblocks.io/hc/en-us/articles/4413379762450). > [!NOTE] > > **Signature caching enabled by default** > > Signature caching for raw signing is enabled by default. [Submit a request to Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=5129187878556\&tf_5465594782876=api_co-signer_setup_and_configuration__what_are_you_trying_to_do___other) if you want this feature disabled. ### IO Exception: status code = 28 This error is a generic Linux filesystem error. It indicates that the disk is full, and your infrastructure and DevOps teams need to add additional disk space. ### SGX Co-signer: failed attestation to Fireblocks, try to perform microcode update This error indicates that the OS or BIOS microcode version is outdated and can’t perform the SGX attestation with the Fireblocks server. #### Resolution Make sure the threads/processor, and hyperthreading (HT) are disabled and that SGX, DCAP, and FLC are enabled. To verify whether HT is enabled, run the following command: ```bash theme={"system"} hostname# lscpu | grep Thread ``` This command should output: ```text theme={"system"} Thread(s) per core: 1 ``` To verify the microcode version, run the following command: ```bash theme={"system"} # cat /proc/cpuinfo | grep -E "model name|microcode" # dmesg | grep microcode # dpkg -l | grep intel-microcode ``` ### SGX Co-signer: migration options when an Azure region outage occurs When Azure region issues occur where an API Co-signer instance is running, it can have the following effects: * Connectivity issues * An instance crashing, becoming unavailable, or being damaged and irrecoverable * Withdrawals are not being processed and are getting stuck in the `PENDING_SIGNATURE` or `QUEUED` statuses due to the API Co-signer being unable to sign transactions. > [!NOTE] > > **Check your Azure region status** > > To check your Azure region's status, visit the [Azure status page](https://status.azure.com/en-us/status). #### Workarounds During an Azure region outage, you can use one of the following workarounds to ensure your financial operations continue uninterrupted. After the outage is resolved, you can return to your normal setup and resume operations. #### Step 1: Install a new API Co-signer instance in another region After doing that, you can unpair your existing API user and pair it with your new Co-signer. #### Step 2: Define a user's mobile device as a designated signer on your Policy You can define a user's mobile device as the designated signer for Policy rules that previously required your Co-signer to sign. However, this solution may not be ideal for some customers since it requires you to manually cancel all already submitted transactions, resubmit them, and then have them signed manually. If you decide to change your Policy rules, [contact Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new). #### Step 3: Migrate an existing Co-signer instance to an unaffected region Follow the [official guide from Microsoft](https://learn.microsoft.com/en-us/azure/site-recovery/azure-to-azure-move-overview). If you require any additional clarification, contact Azure for technical assistance. Note that this operation might take a considerable amount of time. #### Checking the instance's health post-migration To verify the Co-signer is healthy after the maintenance reboot, run `docker ps -a` and check the output. You should be able to confirm that the `cosigner-init` container is not running and the `cosigner` container is not restarting. Also, cancel any transactions that are stuck in the `PENDING_SIGNATURE` status and verify that the status of your `QUEUED` transactions have changed to `PENDING_SIGNATURE`, meaning that the Co-signer can process them. ### SGX Co-signer: setup fails with error "Failed to download loader enclave" This error typically occurs during the initial setup process of an API Co-signer. During the initial setup process, the First user on this machine checkbox must be selected when the first API user to be added to the Co-signer machine is created. If the API user is created and the checkbox is not selected, the error below appears in the `date_time_run.log` file (located in the same directory in which the API Co-signer script runs): ```text theme={"system"} customer_cosigner:24 ERROR 31/12/2000 20:03:21,462 untrusted/ra_loader.cpp(457)fireblocks::common::ra_loader::download_loader_with_access_token - Failed to download loader enclave file, http status 401 customer_cosigner:24 FATAL 31/12/2000 20:03:21,463 main.cpp(490) ::init - Failed to download ra loader ``` #### Resolution There are two resolutions for this issue. #### Resolution 1: Start over with a new API user 1. Delete the API user that you attempted to pair with the Co-signer. 2. Create another API user. Make sure to select the **first user in this machine** checkbox. 3. Run the following command on the Co-signer instance: ```bash theme={"system"} ./cosigner stop docker kill $(docker ps -q) docker rm $(docker ps -aq) docker rmi $(docker images -q) ``` 4. Run the following command to begin the setup process again: ```bash theme={"system"} ./cosigner setup ``` #### Resolution 2: Contact Fireblocks Support If you cannot create an API user or require the setup to be completed with the existing API user, contact Fireblocks Support. ### SGX Co-signer: syntax error near unexpected token: “newline” or “ `` ” This error indicates that the link for downloading the Co-signer script has expired. To confirm, run the following command: ```bash theme={"system"} head cosigner ``` If the link has expired, you should get the following response: ```xml theme={"system"} `` SignatureDoesNotMatch`` The request signature we calculated does not match the signature you provided. Check your key and signing method.`` AKIA5QO5IU7PYPUJ7AU6`` AWS4-HMAC-SHA256 ``` #### Resolution To resolve this issue, download a new SGX Co-signer install script from the Console (under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer)) and run it again. ### SGX Co-signer: setup fails with the error "gathering device information while adding custom device, "/dev/sgx\_enclave", or unable to find file or directory" This error indicates that the target instance doesn't support SGX or its SGX is not enabled, or that other machine specs don't match or are missing. For example, the correct size should be `DC4s_V3`. #### Resolution * You can find a list of SGX-supported instance types [here](https://docs.microsoft.com/en-us/azure/confidential-computing/quick-create-portal). * For product availability in different regions, see [here](https://azure.microsoft.com/en-us/global-infrastructure/services/?products=virtual-machines\®ions=all). ### SGX Co-signer: invalid reference format This error indicates that you upgraded an existing Azure instance with an older version installed, and the older version files are stored in your temp files (.local\_config / .revision). #### Resolution To resolve this issue: 1. Make sure to keep the docker-compose files and the script in the correct directory (`.local_config` / `.revision`) before performing the API Co-signer setup. 2. Remove all files in the directory except the docker-compose files and the script. 3. Run the following command: ```bash theme={"system"} ./cosigner setup ``` 4. When you are prompted to pair the token, exit that installation but continue with the docker installation. ### SGX Co-singer: curl command to [https://mobileapi.fireblocks.io/broadcast\\\_mpc\\\_m](https://mobileapi.fireblocks.io/broadcast\\_mpc\\_m) failed with error "Couldn't resolve host 'mobile-api.fireblocks.io'" > [!WARNING] > > This error is not common, so we recommend contacting Fireblocks Support before you take action. This error indicates that the request failed to send a message for the signed transaction and will remain in the **Pending** status until it is canceled. This is typically due to a network issue. However, in one case, we found that the DNS prevented the message from reaching the signing phase. #### Resolution To resolve this issue: 1. Go to the `vi/etc/systemd/resolved.conf` directory. 2. Under the Resolve heading, change the DNS value to **8.8.8.8 8.8.4.4**. 3. Run the following command: ```bash theme={"system"} systemctl restart systemd-resolved.service ``` # Configuring Multiple API Co-signers in High Availability Source: https://developers.fireblocks.com/cosigners/operations/multiple-cosigners-high-availability ## Overview You can configure multiple API Co-signers to operate in an active-active state, ensuring transaction operations align with your business continuity requirements. If one API Co-signer fails or becomes impaired, the remaining Co-signer(s) will continue signing transactions seamlessly. ## Configuring multiple Co-Signers to work in parallel Upon completing the Co-signer's setup and configuration, you can configure them to work in parallel using the Policies. Each API Co-signer must have at least one API user assigned the Signer role. These API users should be added to the **Designated Signers / Groups** field in the Policy for the transaction types the Co-signers are authorized to sign. You can add API users individually or as part of a user group. All designated signers for the rule must be API users. A combination of API users and Fireblocks Console users is not permitted. Refer to the [following article](https://support.fireblocks.io/hc/en-us/articles/29184395887772-About-Policies) to learn more about creating and modifying your Policies. Once the Owner and Admin Quorum approve the Policy update, multiple Co-signers operate in parallel. Now, the transaction signing process follows these steps: 1. A transaction is initiated using the Console or APIs. 2. Fireblocks evaluates the transaction against the workspace's Policy to match it with the appropriate rule. The rule includes API users in the Designated Signers/Groups field. 3. Fireblocks calls all the Co-signers that are paired with the API users listed in the Policy rule's Designated Signers/Groups field to confirm their availability for signing. 4. Fireblocks identifies the first available API user with a paired Co-signer and sends the transaction to that Co-signer for signing. 5. If a Callback Handler is configured for the API Co-signer, it determines whether the transaction is signed or rejected based on the defined callback logic. If no Callback Handler is configured, the API user signs the transaction automatically. ## Configuring Policy rules for a group of API Co-signers ### Exchange or fiat accounts You cannot add multiple API users or a user group to the Designated Signers/Groups field in rules in which the Source field contains an exchange or fiat account. Doing so causes transactions that match that rule to fail automatically. You must assign a single API user in Policy rules for exchange and fiat accounts. ### Policy Configuration Depending on how you want to customize the Policy (i.e., whether you create inclusionary or exclusionary rules), you can use different methods to ensure transactions match the correct rule. In the following examples, Group 1 members are the API Co-Signers. #### Method 1: Including API Co-Signers only for supported sources | Rule | Initiator | Type | Source | Dest. | Whitelisted / OTA | Amount | Time Period | Asset | Action | Approved By | Designated Signers / Group | | :- | :- | :- | :- | :- | :- | :- | :- | :- | :- | :- | :- | | 1 | Any | Tx | Any VA | Any | Whitelisted + OTA | \$0 | Single Tx | Any | Allow | - | Group 1 | | 2 | Any | Tx | Any | Any | Whitelisted + OTA | \$0 | Single Tx | Any | Approved By | Tywin | - | The rules in the table above state: * Rule 1: This rule allows any single transaction from any vault account to any whitelisted destination or one-time address with an amount greater than \$0 from any asset. However, all transactions that match this rule must be signed by one of the Group 1 members. * Rule 2: This rule allows any single transaction from any source to any whitelisted destination or one-time address with an amount greater than \$0 from any asset. However, all transactions that match this rule must be approved by Tywin. Therefore, according to the first match principle, the API Co-signers only sign single transactions in which the source is a vault account. #### Method 2: Excluding API Co-Signers from unsupported sources | Rule | Initiator | Type | Source | Dest. | Whitelisted / OTA | Amount | Time Period | Asset | Action | Approved By | Designated Signers / Group | | :- | :- | :- | :- | :- | :- | :- | :- | :- | :- | :- | :- | | 1 | Any | Tx | Any Exchange, Any Fiat | Any | Whitelisted + OTA | \$0 | Single Tx | Any | Approved By | Tywin | - | | 2 | Any | Tx | Any | Any | Whitelisted + OTA | \$0 | Single Tx | Any | Approved By | Tywin | Group 1 | The rules in the table above state: * Rule 1: This rule allows any single transaction from any exchange or any fiat account to any whitelisted destination or one-time address with an amount greater than \$0 from any asset. However, all transactions that match this rule must be approved by Tywin. * Rule 2: This rule allows any single transaction from any source to any whitelisted destination or one-time address with an amount greater than \$0 from any asset. However, all transactions that match this rule must be approved by Tywin and then signed by one of the members from Group 1. Therefore, according to the first match principle, the API Co-Signers only sign single transactions in which the source is not an exchange or a fiat account. ## Multiple API Co-signers deployment options Co-signers can be deployed either in the cloud or on-premise, depending on your requirements. For example, you can implement a cluster with three Co-signers in an active-active-passive architecture as follows: * **Active-Active Co-signers:** Two API Co-signers are deployed in separate availability zones within a cloud service provider. This configuration eliminates a single point of failure and ensures continuous operation without downtime. * **Passive Co-signer:** A third API Co-signer is deployed on-premise as a failover. This on-premise Co-signer ensures redundancy, allowing for uninterrupted operation during updates or maintenance of the cloud-based Co-signers. ## Backup & Recovery Backing up the API Co-signer server and data is highly recommended for streamlining machine updates or replacement processes. This backup can also be used for disaster recovery (DR), however, it is recommended to set up an additional API Co-signer in high availability (active-active) and use it if something goes wrong. You can learn more about backing up and restoring the API Co-signer by referring to one of the articles below: * [SGX API Co-Signer Backup & Recovery](/cosigners/sgx/api-cosigner-maintenance-sgx#migrate-to-a-new-machine) * [AWS Nitro API Co-Signer Backup & Recovery](/cosigners/aws/api-cosigner-maintenance-aws-nitro#migrate-to-a-new-machine) ## Best practices To ensure business continuity, install the API Co-signers in high availability using both on-prem data centers (when possible) and cloud service providers. If only one infrastructure provider is available for hosting all the API Co-signers, follow the best practices for each cloud service provider for setting up cross-region replication and multiple API Co-signer instances on different data centers when deploying on-prem. # Install an API Co-signer Source: https://developers.fireblocks.com/cosigners/overview/api-cosigner-installation-flow Register an API Co-signer in the Console, copy the pairing token and installation script, then install it on your platform. Install an API Co-signer in three stages: prepare the platform, register the Co-signer in the workspace and copy the pairing token, then run that platform's installation script. ## Prepare the platform Set up the resources the Co-signer needs, including network access to the domains required for installation and operation. Follow the **Install** page in your platform's group for those steps. ## Get the pairing token Create a new API user for the Co-signer. The first pairing needs admin access to the Fireblocks Console, and the workspace Owner must be available to approve the configuration change. During installation you will use two items from the Console. Copy both before you start: * The API user's pairing token * The download link for the installation script that matches your Co-signer type: Intel SGX, AWS Nitro, or Google Cloud Confidential Space ### Add an API user Add an API user in the **API users** tab of the Console's Developer Center, or with the [create API user](https://swagger.fireblocks.com/#/Api%20User/createApiUser) API. This API user is how the Co-signer connects to the workspace. * Enter a name of up to 30 characters * Select the API user's role * Attach [a certificate signing request (CSR) file](/docs/generate-a-csr-for-an-api-user) > [!NOTE] > > The Co-signer does not use the CSR to connect to the workspace. You still provide one, because the same API user can make API calls. ### Add the Co-signer entry Add the Co-signer in the **Co-signers** tab of the Console's Developer Center, or with the [Co-signer APIs](https://swagger.fireblocks.com/#/Cosigners%20\(Beta\)). Select **Add co-signer**, then: * Enter the Co-signer's name * Select **Install a new co-signer on the local machine**, then select **Continue** * Choose an API user that is not already paired with a Co-signer * Select **Add** The Co-signer appears in the **Co-signers** tab as offline. It connects to the workspace only after you finish installation. ### Copy the pairing token and the installation script Select **Pair API user** on the new Co-signer, then: * Copy the API user's pairing token. The token is valid for one hour. * Copy the download link for your Co-signer type's installation package, under [**Settings** > **General** > **API co-signer**](https://console.fireblocks.io/v3/settings/general/api-co-signer). The link is valid for seven days. > [!NOTE] > > If you cannot find the installation script download link in the Console, [contact Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new). ## Install and connect On the Co-signer machine, download the installation package from the link you copied and run the installation script. The **Install** page for your platform has the commands and the resources that script expects. You will enter the pairing token during installation. ## Designate the signer in a Policy rule After the Co-signer is connected, [designate the paired API user](https://support.fireblocks.io/hc/en-us/articles/29211687914268-Policy-rule-parameters#h_01KYS72RFHKWNZVCZGAMX77KAK) as the signer or an approver on a [Policy rule](https://support.fireblocks.io/hc/en-us/articles/29184395887772-About-Policies). When a transaction or configuration change matches that rule, the Co-signer paired with that API user signs or approves it. See [how roles and Policy rules work with a Co-signer](/cosigners/overview/cosigner-architecture-overview#configuring-your-workspace-to-sign-or-approve-using-an-api-co-signer). # API Co-signer Security Checklist and Recommended Defense and Monitoring Systems Source: https://developers.fireblocks.com/cosigners/overview/co-signer-security-checklist-defense-monitoring ## Co-signer security checklist * Only **authorized personnel** with **high-level privileges** and **full trust** from your organization may perform the Co-signers installation. * Use a clean, hardened machine for the Callback Handler server, restricting access exclusively to authorized personnel or service accounts. * Configure your network rules, cloud resources, and required policies according to the instructions provided in each API Co-signer installation guide. * Use the Callback Handler to log all approval requests, and consider utilizing it to implement additional programmatic protection logic against malicious withdrawals. * Create Policy rules that prevent API users from initiating transfers above a specific amount threshold within a certain timeframe, and require additional manual approval. These rules should apply globally to all withdrawals and withdrawals from specific external user wallets. * Fireblocks advises against disabling Linux UEFI secure boot on your API Co-signer virtual machine, as this goes beyond the security risks introduced by not validating kernel code. We recommend working around any issues you have instead. Using TrendMicro Deep Security agent on Ubuntu 20.04 is one option for secure boot support. ## Co-signer recommended defense and monitoring systems Although a quorum can be configured to approve requests, and a single MPC key share cannot be used to compromise the system, we recommend adding multiple defense and monitoring systems on Fireblocks API Co-signer instances. Implementing the recommended defense and monitoring systems can significantly improve the security of the Fireblocks API Co-Signer and reduce the risk of security incidents. * **Cloud Workload Protection**: A solution that actively monitors the instance running on the Fireblocks AWS API Co-Signer and provides real-time protection against known and unknown threats. * **Event Detection and Response (EDR)** or **Extended Detection and Response (XDR)**: A solution that actively monitors the instance running on the Fireblocks AWS API Co-Signer and detects and responds to potential security threats in real-time. * **Security Information and Event Management (SIEM)**: A solution to collect all login attempts to the instance running on the Fireblocks AWS API Co-Signer and provides real-time alerting and reporting on potential security incidents. * **Privileged Access Management (PAM)**: A solution that actively controls and monitors access to privileged accounts, such as root access to the instance running on the Fireblocks AWS API Co-Signer. A PAM solution can also provide real-time monitoring and alerting on privileged account activity, and enforce security policies, such as password management and least privilege access. * **Multi-Factor Authentication (MFA)**: An MFA solution can enforce secure authentication and access control to the instance running on the Fireblocks AWS API Co-Signer. An MFA solution can also help prevent unauthorized access and reduce the risk of account compromise. # API Co-signers Architecture Overview Source: https://developers.fireblocks.com/cosigners/overview/cosigner-architecture-overview ## How automated signing and approvals work ### Overview The API Co-signer automates two things that otherwise require manual user input on the Fireblocks mobile app: * **Transaction signing** — a paired Signer or Admin API user contributes its MPC key share so the Co-signer can sign transactions on its behalf. This is the multi-signer MPC process the Fireblocks mobile app also participates in, with a set of independent Cloud Co-signers operated by Fireblocks each holding a key share alongside the API Co-signer. Refer to [About Fireblocks MPC](https://support.fireblocks.io/hc/en-us/articles/6984668676124-MPC-CMP) for background on the underlying MPC signing algorithm. * **Transaction and configuration approvals** — a paired Non-Signing Admin or Approver API user casts approval decisions on transactions and, for Non-Signing Admins, on workspace configuration changes. Each user has their own approval key that the Co-signer uses to sign the approval; approvals do not run through MPC. By hosting the Co-signer in your environment and pairing it with API users, you can configure the [Policies](https://support.fireblocks.io/hc/en-us/articles/29184395887772-About-Policies) to [designate the API user](https://support.fireblocks.io/hc/en-us/articles/29211687914268-Policy-rule-parameters#h_01KYS72RFHKWNZVCZGAMX77KAK) paired with the Co-signer as either the signer or an approver on rules that match your criteria. The Co-signer then signs or approves those requests automatically, without a human tapping the mobile app. A Co-signer fits workspaces with high transaction volume or frequent activity. To run either automation, host the API Co-signer in your environment on a machine or virtual machine (VM) that supports enclaves. An enclave is a secure runtime that isolates and protects data and code, including from privileged users on that machine. Fireblocks API Co-signers operate on Intel SGX, AWS Nitro, or Google Cloud Confidential Spaces enclaves. They can be deployed on major cloud platforms such as Azure, AWS, Google Cloud, IBM Cloud, and Alibaba Cloud, as well as on-premises with Intel SGX-capable servers. > [!NOTE] > > Fireblocks has fully integrated AWS Nitro Enclave technology. If you are currently using KMS-based AWS API Co-signers, it is recommended to upgrade to the latest Nitro version for enhanced security and performance. Observe the diagram below, which illustrates how a transaction or configuration-change request flows from the console or API through Fireblocks' Policy Service, Configuration Manager, Signing Services, and Approval Service, and how it reaches the customer's API Co-signer, mobile device, API users, and the Fireblocks Cloud Co-signers #1 and #2. ![Diagram of the Fireblocks platform's request flow and API Co-signer architecture: a console or API request either enters the Policy Service (transactions, routed to Signing Services or Approval Service) or the Configuration Manager (configuration changes, routed directly to Approval Service); Signer/Admin API users participate in MPC signing with Fireblocks Cloud Co-signers, and Non-Signing Admin/Approver API users cast approvals with their own approval key, both via the Mobile & API Co-signer Gateway and, optionally, a Callback Handler.](https://mintcdn.com/fireblocks-43c4b3ee/KchmYsGrJzEFoBYG/images/docs/cosigner-architecture-overview-signer-approver-roles.png?fit=max\&auto=format\&n=KchmYsGrJzEFoBYG\&q=85\&s=b0b4bab48ffc61c897bbd6a626fa6be9) Refer to the articles below for detailed information on the specific architecture of each type of enclaved Co-signer: * [Intel SGX Co-signer architecture running in Azure, IBM Cloud or Alibaba Cloud](/cosigners/sgx/intel-sgx-api-co-signer) * [Nitro Co-signer architecture running in AWS](/cosigners/aws/aws-nitro-api-co-signer) * [Confidential Space Co-signer architecture running in Google Cloud](/cosigners/google-cloud/gcp-confidential-space-api-co-signer) ### Using API users to connect the Co-signer to your workspace To connect a new or existing Co-signer to your workspace, pair it with an API user from the workspace. Connecting an existing Co-signer with another workspace is supported for AWS Nitro, SGX, Azure; not supported for GCP. Existing Co-signers are typically associated with another workspace within your organization. Pairing the Co-signer is performed using a JWT-encoded Pairing Token obtained from the Console for a specific API user. This token is used during Co-signer installation to pair the initial API key, enabling communication with Fireblocks' SaaS. The Co-signer is identified exclusively by the workspace and the API user used to establish the connection. The pairing process for the first API user requires admin-level access to the Fireblocks Console and root access to the Co-signer’s VM. During this process, you will provide the Co-signer with a pairing token linked to the API user, enabling the platform to recognize the Co-signer and associate it with the Workspace. You can pair multiple API users to a single Co-signer. Additionally, API users from different Workspaces can be paired with the same API Co-signer, except when using the GCP Confidential Space Co-signer. ### Connecting your business logic to the Co-signer You can optionally connect your business logic to the Co-signer through a feature called a Callback Handler, configured per API user. The Callback Handler is an HTTPS server that receives requests from the Co-signer at a designated endpoint whenever a signing request (for Signer or Admin API users) or an approval request (for Non-Signing Admin or Approver API users) is triggered. The Callback Handler responds with an action, such as approving or denying the request. If no Callback Handler is configured, the Co-signer automatically signs or approves all requests for that API user. Within a single Co-signer, some API users can operate with a configured Callback Handler, while others can function without it. When configuring a Callback Handler for a paired API user, you specify the URL of the Callback Handler server and select a secure communication method to establish a secure channel. > [!NOTE] > > **Cold workspaces** > > In cold workspaces, the API Co-signer participates in the approval stage only. Transaction signing is performed offline using the cold mobile app, so the Co-signer never signs. The API user roles that can be paired with a Co-signer in a cold workspace, and therefore can have a Callback Handler, are: > > * **Non-Signing Admin** — transaction approvals and configuration change approvals. > * **Approver** — transaction approvals. > > The Callback Handler fires during approval requests for these users. Refer to [Setup API Co-signer Callback Handler](/cosigners/callback-handler/create-api-co-signer-callback-handler) to learn more about it. ### Callback best practices When a signing or approval request reaches the API Co-signer, the callback is invoked with the request details. We recommend closely monitoring this communication to ensure the callback responds quickly and reliably, as the Co-signer does not sign or cast its approval until the callback approves the request. ## Installing an API Co-signer ### Available API Co-signer types Fireblocks provides multiple deployment options for API Co-signers, available in both cloud environments and on-premises, in regions that meet the necessary enclave technology requirements. These deployment options leverage various enclave technologies to safeguard the API user keys stored in the Co-signer (MPC key shares for Signers and Admins, approval keys for Non-Signing Admins and Approvers), allowing you to select the solution that aligns best with your production environment requirements. For the Console steps, the pairing token, and where to go next, see [Installation](/cosigners/overview/api-cosigner-installation-flow). The commands for each platform are on that platform's Install page. ### High-throughput workspaces For high-throughput workspaces, we recommend setting up more than one API Co-Signer. This setup enables both high availability and load balancing between Co-Signers, allowing for higher overall throughput. The required number of Co-Signers is determined by the number of in-flight transactions in the signing phase. As a rule of thumb, maintain one Co-Signer (with a single vCPU) per every 2.5 TPS. **Learn more** * [Configuring Multiple API Co-signers in High Availability](/cosigners/operations/multiple-cosigners-high-availability) * [Installation](/cosigners/overview/api-cosigner-installation-flow) ### Host type recommendations We recommend using at least a **Standard\_DC4s\_v3** instance on Azure. AWS and GCP instance recommendations are TBD. In addition, we recommend using the latest Co-signer version. The table below summarizes key considerations per cloud provider: | Provider | Key Considerations | | :- | :- | | **Azure (SGX-based)** | Provides hardware security at the highest security level, relying only on Fireblocks and Intel. Because SGX is CPU-based, it has performance limitations including reduced speed, limited memory, and a fixed number of threads. As a result, it is the slowest option. | | **AWS Nitro** | Requires trust in AWS for both hardware and security. This is still a confidential computing solution, but the cloud provider is part of the threat model. AWS is a highly reputable, publicly traded company with strong engineering practices. | | **GCP** | Recommended if you are required to use Google Cloud. Like AWS Nitro, this is a confidential computing solution where the cloud provider is part of the threat model. | **Best practices** * For **maximum security:** select Azure + SGX * For **best performance:** select AWS + Nitro ### Available enclave types Learn more about the enclave technologies Fireblocks uses by referring to the following resources: * [Microsoft Azure SGX Enclave-capable Confidential Compute VM](https://learn.microsoft.com/en-us/azure/confidential-computing/confidential-computing-enclaves) * On-Premise [Intel SGX-enabled Processors](https://www.intel.com/content/www/us/en/architecture-and-technology/software-guard-extensions-processors.html) * [AWS Nitro Enclave-capable EC2 instance](https://aws.amazon.com/ec2/nitro/nitro-enclaves/) * [Google Cloud Confidential Space workload container](https://cloud.google.com/confidential-computing/confidential-space/docs/confidential-space-overview) * [IBM Cloud Bare Metal Servers SGX-capable machine](https://cloud.ibm.com/docs/bare-metal?topic=bare-metal-bm-server-provision-sgx) * [Alibaba Cloud Intel SGX-capable Elastic Compute Service (ECS) instance](https://www.alibabacloud.com/help/en/ecs/user-guide/build-an-sgx-encrypted-computing-environment) ## Configuring your workspace to sign or approve using an API Co-signer To enable the API Co-signer to participate in signing or in approvals, pair it with an API user from your workspace. Pairing generates the keys that user needs to act, and stores them securely inside your Co-signer. The API user's role determines which keys are generated and what the Co-signer can automate on its behalf: * **Signer or Admin** — pairing generates a unique MPC key share (stored inside your Co-signer, with matching shares on Cloud Co-signers #1 and #2) plus a workspace configuration key. The Co-signer can sign transactions on the API user's behalf through the multi-signer MPC process, and Admins can also cast configuration change approvals. * **Non-Signing Admin** — pairing generates the API user's approval key inside your Co-signer. The Co-signer can automate the API user's approvals on transactions and on workspace configuration changes. It cannot sign. * **Approver** — pairing generates the API user's approval key inside your Co-signer. The Co-signer can automate the API user's approvals on transactions. It cannot sign. Refer to [User roles](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles) for the full role reference. To activate the automation, configure the [Policies](https://support.fireblocks.io/hc/en-us/articles/29184395887772-About-Policies) to [designate the paired API user](https://support.fireblocks.io/hc/en-us/articles/29211687914268-Policy-rule-parameters#h_01KYS72RFHKWNZVCZGAMX77KAK) as the signer or as an approver on the rules you want to automate. When a transaction or configuration change matches a rule, the request is routed to the Co-signer paired with that API user. The end-to-end flow using the Fireblocks REST API, the API Co-signer, and the optional Callback Handler proceeds as follows: 1. A new transaction or configuration change is initiated using the Fireblocks API and generated on the backend. 2. The request is evaluated against your Policies to determine who must sign and approve it. 3. If an API user paired with an API Co-signer is designated in the Policy as the signer or as an approver, the request is sent to the Co-signer associated with that API user. 4. The API Co-signer, hosted in your secure environment, polls the Fireblocks SaaS for pending requests. If a request is available, the SaaS immediately provides the request details. 5. If a Callback Handler is configured for that API user, the API Co-signer sends a secure request to the Callback Handler for a decision. 6. If the Callback Handler responds with "approved," the Co-signer completes the request: for Signer or Admin API users it signs the transaction in coordination with Fireblocks' Cloud Co-signers through the MPC process, and for Non-Signing Admin or Approver API users it uses the API user's approval key to sign the approval. If the response is "rejected," the request is declined. If the Callback Handler does not respond within 30 seconds or returns "retry," the request fails. Refer to [Setup API Co-signer Callback Handler](/cosigners/callback-handler/create-api-co-signer-callback-handler) to learn more. ## Manage paired API users After the Co-signer is connected, you can pair additional API users, configure a Callback Handler, unpair an API user, or rename the Co-signer. See [Operating the API Co-signer](/cosigners/operations/api-cosigner-operate). ## Communal Test Co-signer A Testnet workspace can use a Communal Test Co-signer that Fireblocks hosts, instead of installing one first. See [Using the Communal Test Co-signer](/cosigners/overview/use-communal-cosigner). # Using the Communal Test Co-signer Source: https://developers.fireblocks.com/cosigners/overview/use-communal-cosigner > [!WARNING] > > 1. **IMPORTANT**: The Communal Callback handler must NOT be used for performance testing. > 2. Due to legal regulations, Fireblocks can not take custodial responsibility for Mainnet workspaces. Therefore, this option is available only to Testnet workspaces. ## Overview Setting up a new Co-signer from scratch can be time-consuming. To accelerate development and prepare your workspace for automation and API-driven workflows, Fireblocks offers the option to use a Communal Test Co-signer before configuring your own. The Communal Test Co-signer is hosted and managed by Fireblocks and serves all customers with Testnet workspaces. Any workspace owner can approve pairing API users with it, generate MPC key share sets, and use it to sign transactions. When you start testing and verifying your signing or approval automation workflows, we recommend setting up your self-hosted API Co-signer instance and replacing the Communal Test Co-signer as soon as your instance is ready. To stop using the Communal Test Co-signer, unpair or delete the API users that were paired with it. *** ## Connecting to the Communal Test Co-signer To use the Fireblocks Communal API Co-Signer, create a new API user using the Console in a testnet workspace and select the option to connect it with the Communal Co-signer. Selecting a user role with [Signer](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles#h_01KS8CGP2QAYH4GCEZ0T598MT7) or [Admin](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles#h_01KS8CGP2P2FR6DY86YSM3QZF4) privileges will allow you to test transaction signing and approving workspace changes. If you only want to test approving workspace configuration changes, you can select the [Non-Signing Admin](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles#h_01KS8CGP2QDZ1NPHVT1B78TSFM) user role. In a [Sandbox workspace](https://support.fireblocks.io/hc/en-us/articles/360020612820-Workspace-types), each API user is automatically paired with the Fireblocks Communal Test Co-signer. This co-signer manages the MPC key shares for all API users in the Sandbox environment across all workspaces. *** ## Testing a Callback Handler with the Communal Test Co-Signer [Set up a Callback Handler](/cosigners/callback-handler/create-api-co-signer-callback-handler), then [configure it](/cosigners/operations/api-cosigner-operate#configure-the-callback-handler) for the API user paired with the Communal Test Co-signer. ### Allowlist the Co-signer IP address The Communal Test Co-signer sends Callback Handler requests from a fixed IP address. Allowlist the IP address for the Fireblocks environment your workspace uses so your Callback Handler can receive those requests. | Environment | IP address | | - | - | | US | `104.45.129.142` | | EU | `172.161.101.44` | | EU2 | `13.80.47.145` | ### Verify Co-signer request signatures The Communal Test Co-signer signs the requests it sends to your Callback Handler. Use the following public key to verify those signatures: ```text Communal Test Co-signer public key theme={"system"} -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqx1xUqdXs3A/VvFrcH1D QN0geYPp7d5NYBOrDMUm0BZl4gUpKDL5OBt4hKr81BSsUXfafMmeyy0/hdFPLrw8 /HnDMPEAhik38dVutNSIu7uOwuk8yyENaxQhQH+hBmUMHiBbMlv0e7YsHLz8QYJi 0fgTRNHekpO7jNB/WaoxlKfMpdpOplcSyN4Oi8828vvBZ7Dj/Ji2AuK+1ECO98RG M7Hu0pT1dS2PnoRF0WdgsDi4a+M+Ya/n90dvAI8AgxwBf/mbmNtNr1e2FPdF2Wat LZTZaGKeh71clgNByJ8qkkJ62LTValNFX+NkdA46jjhm7L8OrNkxFBtxBHQ5aQ3x QwIDAQAB -----END PUBLIC KEY----- ``` *** ## Known limitations For UTXO transactions, such as `BTC_TEST` in testnet workspaces, the Fireblocks Communal Test Co-Signer supports a maximum of 50 transaction inputs. The first 50 inputs from the transaction source vault account are selected automatically based on the requested amount. For all other workspaces and signing configurations, up to 250 transaction inputs are supported for Bitcoin transactions. If a transaction signed by the Fireblocks Communal Test Co-Signer fails because it requires more than 50 inputs, the transaction substatus will be `INTERNAL_ERROR`. # Add a Hedera Asset Wallet Source: https://developers.fireblocks.com/docs/add-a-hedera-asset-wallet Create HBAR and Hedera Token Service (HTS) asset wallets in a vault account, and choose how each HTS token is associated with the account on-chain. ## Overview On Hedera, an account must be associated with a Hedera Token Service (HTS) token on-chain before it can receive that token. Creating an HTS asset wallet in Fireblocks does not perform this association by default. Fireblocks creates an internal representation of the wallet only, and the token is associated on-chain later depending on the funding option you choose. All HTS asset wallets in a vault account share the vault's base HBAR deposit address. This behavior mirrors Solana, where each SPL token requires its own on-chain token account that is commonly funded by the first sender. See [Create Direct Custody Wallets](/docs/create-direct-custody-wallets) for how this compares to other blockchains. ## Add an HBAR asset wallet to a vault account To add an HBAR asset wallet using the Fireblocks Console: 1. Go to **Accounts** > **Vault** in your Fireblocks Console. 2. Select the vault account to which you want to add the HBAR asset wallet. 3. Select **+ Create Wallet**. 4. In the Add Asset Wallet window, select **HBAR** from the drop-down list. You can also begin typing the asset's name to search for it. You can add only one wallet per asset type per vault account. 5. Select **Add**. You can also create the wallet with the [Create Vault Wallet](/reference/create-vault-wallet) API flow. ## Add an HTS asset wallet to a vault account Create the HTS asset wallet the same way as the HBAR wallet above, selecting the HTS token from the drop-down list. Creating the wallet registers the asset internally in Fireblocks. No on-chain association transaction is submitted at that point. The token association fee is covered by one of two funding options: 1. **First sender covers the fee:** The fee is deducted when the first incoming transaction occurs. No upfront action is required. 2. **Fund from the vault HBAR balance:** The fee is deducted from the vault's HBAR balance immediately after wallet creation. This option requires transaction signing. When creating the wallet via API, set the `blockchainWalletType` parameter to select the funding option. See [Hedera HTS tokens](/reference/create-vault-wallet#hedera-hts-tokens) for details. > [!NOTE] > > Not all senders support transfers to accounts that are not yet associated with the token. Fireblocks supports outgoing transfers to destinations that are not associated with the token. ## HBAR wallets created before July 2026 HBAR wallets created before July 2026 do not support sender-funded association. For these wallets, associate each HTS token before receiving deposits by creating the token wallet with the **Fund from the vault HBAR balance** option. ## Deposits to unassociated accounts Deposits to an account that is not associated with the token, where the sender does not cover the association fee, fail on-chain with the `TOKEN_NOT_ASSOCIATED_TO_ACCOUNT` error. If a deposit fails with this error, associate the token with the account and ask the sender to retry the transfer. ## Listing HTS tokens To add an unlisted HTS token to your workspace, contact Fireblocks Support with a link to the token's page on the relevant block explorer. [Learn more about adding unlisted tokens](/docs/add-your-tokens-1). For token operations that are not yet natively available through the Fireblocks Console and APIs, see the [Hedera Token Service SDK](/reference/hedera-token-service-sdk). # Add an Exchange Account Source: https://developers.fireblocks.com/docs/add-an-exchange-account You can easily add your own exchange account to the Fireblocks workspace, provided it's supported by Fireblocks. Once connected, you can seamlessly transfer funds between your exchange account and Fireblocks via the console or API. This guide will explain to you how to add an exchange account via our TypeScript and Python SDK. ## Prerequisites 1. Contact your CSM to enable this feature and get access to: 1. [Get Exchange Accounts Credentials Public Key endpoint](/reference/getexchangeaccountscredentialspublickey) 2. [Add an Exchange Account endpoint](/reference/addexchangeaccount) 2. Make sure to prepare your workspace and perform all the steps necessary [here](/reference/quickstart). 3. Install all the prerequisites for our [TypeScript](/reference/typescript-sdk) and [Python](/reference/new-python-sdk) SDK. ## Add an Exchange Account via SDK You can use the following code to add an exchange account > [!NOTE] > > This endpoint currently only supports the following exchanges `INDEPENDENT_RESERVE`,`BIT`, `BITHUMB`, `BITSO`, `CRYPTOCOM`, `BYBIT_V2`, `WHITEBIT` , `HITBTC`, `GEMINI`, `HUOBI`, `GATEIO`, `COINHAKO`, `BULLISH`, `BITGET`, and `LUNO` > [!NOTE] > > If you're connecting a sub-account (for example, on exchanges that support account hierarchies), include `subAccountId` in the credentials object. This field is required for sub-account connections and should be set to your exchange sub-account ID. ```python Python theme={"system"} import base64 import json from fireblocks.client import Fireblocks from fireblocks.client_configuration import ClientConfiguration from fireblocks.base_path import BasePath from fireblocks.models.add_exchange_account_request import AddExchangeAccountRequest from cryptography.hazmat.primitives.serialization import load_pem_public_key from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes from pprint import pprint my_api_key="your_api_key" with open('your_secret_key_file_path', 'r') as file: secret_key_value = file.read() configuration = ClientConfiguration( api_key=my_api_key, secret_key=secret_key_value, base_path=BasePath.US #Make sure to use the correct environment. Please see the note ) with Fireblocks(configuration) as fireblocks: tenant_id = None public_key = None try: # Get exchange accounts credentials public key api_response = fireblocks.exchange_accounts.get_exchange_accounts_credentials_public_key().result() tenant_id = api_response.data.tenant_id public_key = api_response.data.public_key except Exception as e: print("Exception when calling ExchangeAccountAPI->get_exchange_accounts_credentials_public_key: %s\n" % e) exit(1) # Credentials encryption exchange_api_key = "your_exchange_account_api_key" exchange_api_secret = 'your_exchange_account_secret_key' credentials = { "apiKey": exchange_api_key, "secret": exchange_api_secret, "tenantId": tenant_id, # Optional for sub-accounts: # "subAccountId": sub_account_id, } pem_public_key = load_pem_public_key(bytearray(public_key, 'utf-8'), default_backend()) credentials_str = bytes(json.dumps(credentials, separators=(',', ':')), 'utf-8') ciphertext = pem_public_key.encrypt( credentials_str, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) encrypted_creds = bytes.decode(base64.b64encode(ciphertext)) # Prepare add exchange account request add_exchange_account_request: AddExchangeAccountRequest = AddExchangeAccountRequest( exchange_type='BIT', name='My BIT account', creds=encrypted_creds, key=exchange_api_key ) try: # Add an exchange account future = fireblocks.exchange_accounts.add_exchange_account(add_exchange_account_request=add_exchange_account_request) api_response = future.result() # Wait for the response print("The response of ExchangeAccountAPI->add_exchange_account:\n") pprint(api_response.data.to_json()) except Exception as e: print("Exception when calling ExchangeAccountAPI->add_exchange_account: %s\n" % e) ``` ```typescript TypeScript theme={"system"} import { readFileSync } from 'fs'; import { Fireblocks, BasePath, ExchangeAccountsApiAddExchangeAccountRequest } from "@fireblocks/ts-sdk"; import { webcrypto } from "crypto"; const crypto = webcrypto; const EXCHANGE_API_KEY = "exchange_api_key"; const EXCHANGE_API_SECRET = "exchange_api_secret"; const FIREBLOCKS_API_SECRET_PATH = "./fireblocks_secret.key"; const fireblocks = new Fireblocks({ apiKey: "my-api-key", basePath: BasePath.US, //make sure to use the correct environment. Please see the note secretKey: readFileSync(FIREBLOCKS_API_SECRET_PATH, "utf8"), }); async function getExchangeAccountsCredentialsPublicKey() { return (await fireblocks.exchangeAccounts.getExchangeAccountsCredentialsPublicKey()).data; } function str2ab(str: string): ArrayBuffer { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; }; function arrayBufferToBase64(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.length; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } async function encryptCredentials(publicKey: string, payload: any) { const pemHeader = '-----BEGIN PUBLIC KEY-----\n'; const pemFooter = '\n-----END PUBLIC KEY-----'; const extractedCode = publicKey.substring(pemHeader.length, publicKey.length - pemFooter.length); const binaryDerString = atob(extractedCode); const binaryDer = str2ab(binaryDerString); const cryptoPublicKey = await crypto.subtle.importKey( 'spki', binaryDer, { name: 'RSA-OAEP', hash: 'SHA-256', }, true, ['encrypt'], ); const payloadStr = JSON.stringify(payload); const encodedDataString = new TextEncoder().encode(payloadStr); const encryptedData = await crypto.subtle.encrypt( { name: 'RSA-OAEP', }, cryptoPublicKey, encodedDataString, ); const encryptedDatab64 = arrayBufferToBase64(encryptedData); return encryptedDatab64; } async function main() { try { const credsPublicKey = await getExchangeAccountsCredentialsPublicKey(); let credentials_payload = { "apiKey": EXCHANGE_API_KEY, "secret": EXCHANGE_API_SECRET, "tenantId": credsPublicKey.tenantId, // Optional for sub-accounts: // subAccountId: SUB_ACCOUNT_ID, }; const encryptedCredentials = await encryptCredentials(credsPublicKey.publicKey, credentials_payload); const request: ExchangeAccountsApiAddExchangeAccountRequest = { addExchangeAccountRequest: { name: "My BIT account", exchangeType: "BIT", creds: encryptedCredentials, key: EXCHANGE_API_KEY } }; const addExchangeResponse = await fireblocks.exchangeAccounts.addExchangeAccount(request); console.log(addExchangeResponse); } catch (e) { console.log(e) } } (async () => { await main(); })(); ``` > [!NOTE] > > Make sure you're using the correct value for the API base URL for your environment: > > * `BasePath.US` - for production workspaces > * `BasePath.Sandbox` - for sandbox workspaces # About Address Registry Screening Source: https://developers.fireblocks.com/docs/address-registry-screening Screen transactions by counterparty identity: create counterparty groups and activate Address Registry Screening through the Fireblocks API. Address Registry Screening (ARS) checks a transaction's source or destination against Fireblocks' shared registry of verified addresses, so you can, for example, reject transactions with unverified counterparties or block restricted jurisdictions. It is free and runs first, before AML/KYT. It is not a risk screening and does not decide whether the Travel Rule applies: a `not found` result means only that the address is not a registered Fireblocks counterparty. ARS needs Address Registry, which every workspace is opted in to by default. If your workspace has opted out, activation fails. See [About Address Registry](/reference/address-registry). ## What you can do through the API | Step | Task | Console | API | | - | - | - | - | | 1 | Create a counterparty group | Yes | [`POST /counterparty_groups`](/api-reference/compliance/create-a-counterparty-group) | | 1 | List counterparty groups | Yes | [`GET /counterparty_groups`](/api-reference/compliance/list-counterparty-groups) | | 1 | Get a counterparty group | Yes | [`GET /counterparty_groups/{groupId}`](/api-reference/compliance/get-a-counterparty-group) | | 1 | Update a counterparty group | Yes | [`PATCH /counterparty_groups/{groupId}`](/api-reference/compliance/update-a-counterparty-group) | | 1 | Delete a counterparty group | Yes | [`DELETE /counterparty_groups/{groupId}`](/api-reference/compliance/delete-a-counterparty-group) | | 2 | Build the Trigger (Screening Policy) and Outcome (Post-Screening Policy) | Yes | No | | 2 | Configure timeout behavior | Yes | No | | 3 | Activate ARS | Yes | [`POST /screening/ars/config/activate`](/api-reference/compliance/activate-ars-address-registry-screening) | | 3 | Deactivate ARS | Yes | [`POST /screening/ars/config/deactivate`](/api-reference/compliance/deactivate-ars-address-registry-screening) | ## Step 1: Create counterparty groups A counterparty group is a named set of jurisdictions; jurisdiction is the only grouping attribute today. Your Outcome rules reference these groups. Create, update, and delete require the Admin or Non-Signing Admin role. Create and update accept an optional `Idempotency-Key` header, valid for 24 hours; delete does not. | Field | Type | Description | | - | - | - | | `groupId` | string (UUID) | Returned by Fireblocks. | | `name` | string | Required. Display name. | | `description` | string | Optional. | | `jurisdictionCodes` | string\[] | Required. ISO 3166-1 alpha-2 codes, for example `["US", "SG"]`. | | `isActive` | boolean | Whether the group is active. | | `createdAt`, `updatedAt` | string (ISO 8601) | Returned by Fireblocks. | ```ts theme={"system"} const { data: group } = await fireblocks.compliance.createCounterpartyGroup({ createCounterpartyGroupRequest: { name: "APAC Financial Partners", jurisdictionCodes: ["SG", "HK"], }, }); console.log(group.groupId); ``` ## Step 2: Build the policy in the Console Under **Policies** > **Compliance**, add Trigger rules for which transactions ARS checks, set the timeout behavior (accept or reject if the check does not return in time), and add Outcome rules: | Condition | Action | | - | - | | Counterparty is in a group | Accept or Reject | | Counterparty found, but in no group | Your catch-all default rule | | Counterparty not found in the registry | Accept or Reject | See [About Address Registry Screening](https://support.fireblocks.io/hc/en-us/articles/30614325399836-About-Address-Registry-Screening) on the Help Center. ## Step 3: Activate ARS Activate or deactivate with the endpoints above. Both return the current configuration: ```json theme={"system"} { "active": true, "lastUpdate": "2026-01-29T12:00:00.000Z" } ``` Once active, ARS applies to every transaction that matches your Trigger. Deactivating stops the check until you activate it again. ## Limitations * `GET /screening/transaction/{txId}` does not include ARS results. A rejection shows as `REJECTED_AML_SCREENING`, the same value as other checks; see [Transaction states](/docs/transaction-screening-operations#transaction-states). * Not supported on European or Swiss-hosted workspaces, or on Developer Sandbox workspaces. Lookups from these workspaces return `not_found`, so your "not found" rule applies. * The registry resolves addresses within the Fireblocks network only. ## On the Help Center To build the ARS policy or manage counterparty groups in the Console, see [About Address Registry Screening](https://support.fireblocks.io/hc/en-us/articles/30614325399836-About-Address-Registry-Screening). # AML/KYT Overview Source: https://developers.fireblocks.com/docs/aml-kyt-overview Compare the AML/KYT providers Fireblocks supports, and see what you can do with AML/KYT screening through the API. AML/KYT screens transactions against your provider's blockchain risk data, including sanctions, darknet markets, stolen funds, and ransomware exposure. The provider returns a risk assessment; your policy decides what happens. It checks any address, regardless of whether the counterparty is on the Fireblocks network. AML/KYT is a premium add-on that Fireblocks Support can enable in your workspace. ## Providers | Provider | Risk result | Outcome actions beyond Accept, Reject, Alert | Workspaces | | - | - | - | - | | [Chainalysis](/docs/chainalysis) | Categorical risk level, plus the risk category matched | None | Testnet, Mainnet | | [Elliptic](/docs/elliptic) | Numeric risk score | None | Mainnet | | [TRM Labs](/docs/trm-labs) (Early Access) | Categorical risk level, plus the risk categories matched | Wait | Mainnet | ## What you can do through the API | Task | Console | API | Guide | | - | - | - | - | | Connect a provider | Yes | No | [Chainalysis](/docs/chainalysis), [Elliptic](/docs/elliptic), [TRM Labs](/docs/trm-labs) | | Read the Trigger and Outcome | Yes | Yes | [Compliance Policies](/docs/compliance-policies) | | Configure outage and delay behavior | Yes | Yes | [Compliance Policies](/docs/compliance-policies) | | Attribute transactions to your customer | No | Yes | [Customer Reference ID](/docs/customer-reference-id) | | Read a transaction's AML/KYT result | Yes | Yes, in the `aml` and `amlList` fields | [Transaction Screening Operations](/docs/transaction-screening-operations) | | Bypass, rescreen, freeze, or unfreeze | Yes, except freeze | Yes | [Transaction Screening Operations](/docs/transaction-screening-operations) | | Switch or disconnect a provider | Through Fireblocks Support | No | | ## Limitations * **One provider per workspace.** Different workspaces can use different providers. * **Coverage varies by provider.** Which blockchains and assets a provider can screen depends on its data coverage, not on Fireblocks. * **Unscreened routes:** vault-to-vault, vault-to-exchange, vault-to-Gas Station, Gas Station-to-vault, and non-custodial wallet to non-custodial wallet within the same workspace. Outgoing transactions on these routes are still registered with your provider and can count toward your usage quota; add a Pass rule to avoid this. * PROGRAM\_CALL and CONTRACT\_CALL transactions and NFTs are not screened; they bypass with UNSUPPORTED\_ASSET. ## On the Help Center To connect a provider or build AML/KYT rules in the Console, see [About AML/KYT](https://support.fireblocks.io/hc/en-us/articles/30614362833308-About-AML-KYT). # Associate End Clients Source: https://developers.fireblocks.com/docs/associating-end-clients-with-transactions You may want to or be required to add end-client identification information to vault accounts and wallets. Depending on the purpose, you can use different endpoints and parameters to do this. # Internal or auditing identification purposes ## Vault accounts When an end client owns (or will own) all the wallets and addresses inside a vault account, call one of the following endpoints: * [Create a new vault account](/reference/createvaultaccount) (for new vault accounts) * [Rename a vault account](/api-reference/vaults/rename-a-vault-account) (for existing vault accounts) Then, use the `name` parameter to enter their identification information (e.g., "Customer\_12345\_Vault"). This propagates the information to every transaction that uses that vault account. > [!WARNING] > > Please make sure to not pass any PII data! ## Wallets When an end client owns (or will own) only one or only a few wallets inside a vault account containing many other wallets, call one of the following endpoints: * [Create an internal wallet](/reference/createinternalwallet) (for new internal wallets) * [Create an external wallet](/reference/createexternalwallet) (for new external wallets) Then, use the `name` parameter to enter their identification information (e.g., "Customer\_12345\_Wallet"). This propagates the information to every transaction that uses that wallet. > [!NOTE] > > Wallets cannot be renamed, so you must create new wallets for existing end client wallets. ## Deposit Addresses When an end client owns (or will own) only one or only a few deposit addresses inside a wallet containing many other addresses, call one of the following endpoints: * [Create a new asset deposit address](/reference/createvaultaccountassetaddress) (for new deposit addresses) * [Update address description](/reference/updatevaultaccountassetaddress) (for existing deposit addresses) Then, use the `description` parameter to enter their identification information (e.g., "Customer\_12345\_Address"). This propagates the information to every transaction that uses that deposit address. # AML/CFT identification purposes > [!WARNING] > > The `customerRefId` parameter should be used *only* for this purpose, as AML providers use it to associate the owner of funds with transactions. To attach your own internal identifier to a specific outgoing transaction (for example, an order or ticket ID), use [`externalTxId`](/reference/api-idempotency#transaction-idempotency) on the create transaction request instead. `customerRefId` is set through the API endpoints listed below. Once assigned to a vault account, wallet, or deposit address, the value automatically propagates to every transaction that uses that entity — both inbound and outbound — so you do not need to include it in the create transaction payload. ## Vault accounts When an end client owns (or will own) all the wallets and addresses inside a vault account, call one of the following endpoints: * [Create a new vault account](/reference/createvaultaccount) (for new vault accounts) * [Set an AML/KYT customer reference ID for a vault account](/reference/setvaultaccountcustomerrefid) (for existing vault accounts) Then, use the `customerRefId` parameter to enter their identification information (e.g., "Customer\_12345\_Vault"). This propagates the vault account's name, the wallet address, the asset name, and the end client’s information to every transaction that uses that vault account. ## Wallets When an end client owns (or will own) only one or only a few wallets inside a vault account containing many other wallets, call one of the following endpoints: * [Create an internal wallet](/reference/createinternalwallet) (for new internal wallets) * [Create an external wallet](/reference/createexternalwallet) (for new external wallets) * [Set an AML/KYT customer reference ID for an internal wallet](/reference/setcustomerrefidforinternalwallet) (for existing internal wallets) * [Set an AML/KYT customer reference ID for an external wallet](/reference/setexternalwalletcustomerrefid) (for existing external wallets) Then, use the `customerRefId` parameter to enter their identification information (e.g., "Customer\_12345\_Wallet"). This propagates the name of the vault account containing the wallet, the wallet address, the asset name, and the end client’s information to every transaction that uses that wallet. ## Deposit Addresses When an end client owns (or will own) only one or only a few deposit addresses inside a wallet containing many other addresses, call one of the following endpoints: * [Create a new asset deposit address](/reference/createvaultaccountassetaddress) (for new deposit addresses) * [Assign AML customer reference ID](/api-reference/vaults/assign-aml-customer-reference-id) (for existing deposit addresses) Then, use the `customerRefId` parameter to enter their identification information (e.g., "Customer\_12345\_Address"). This propagates the name of the vault account containing the wallet, the wallet address, the description of the deposit address, the asset name, and the end client's information to every transaction that uses that deposit address. # Audit Logs API Source: https://developers.fireblocks.com/docs/audit-logs-api The Audit Logs API returns a structured record of events that occurred in your Fireblocks workspace. Use it to feed compliance tooling, SIEM platforms, monitoring dashboards, and automation pipelines. For the full endpoint reference, see [Get audit logs](/api-reference/audit-logs/get-audit-logs). > [!NOTE] > > If you are already using the legacy `timePeriod` and `cursor` parameters, your integration continues to work without code changes. *** ## Endpoint ```text theme={"system"} GET /v1/management/audit_logs ``` *** ## Permissions The caller's API key must be associated with one of the following workspace user roles: * Admin * Non-Signing Admin * Auditor * Security Admin * Security Auditor Any other role returns a `403 Forbidden` error. *** ## Query parameters ### Date range | Parameter | Type | Description | | :- | :- | :- | | `startTime` | integer (epoch ms) | Start of the query window. | | `endTime` | integer (epoch ms) | End of the query window. Must be strictly greater than `startTime`. Defaults to the current time when omitted. | If neither `startTime` nor the deprecated `timePeriod` are supplied, the endpoint returns events from the last 24 hours. **Example:** ```text theme={"system"} ?startTime=1735689600000 // 2026-01-01T00:00:00Z &endTime=1738367999000 // 2026-01-31T23:59:59Z ``` *** ### Filters | Parameter | Type | Description | | :- | :- | :- | | `category` | string | The event category. See [Filter values](#filter-values). | | `subject` | string | The event subject. | | `event` | string | The event type. | | `user` | string | The user name that triggered the event. | | `userId` | string | The ID of the user who triggered the event. | **Usage notes:** * Each filter accepts one or more values by repeating the query key. * Values are case-sensitive and must match exactly. * Values that contain spaces or special characters must be URL-encoded — for example, `Outgoing transaction` becomes `Outgoing%20transaction`. **Combination logic:** * Multiple values for the **same** field combine as **OR**. For example, `?category=Administration&category=Security` returns events in either category. * Values across **different** fields combine as **AND**. For example, `?category=Security&userId=abc123` returns events that are in the Security category **and** triggered by user `abc123`. * OR logic across different fields is not supported. You cannot retrieve events that are either by user X or in category Y in a single query. *** ### Sorting and pagination | Parameter | Type | Default | Description | | :- | :- | :- | :- | | `order` | string (`ASC` or `DESC`) | `DESC` | Sort direction. Case-sensitive. Results are always sorted by event timestamp. | | `pageSize` | integer | `200` | Number of results per page. Minimum `1`, maximum `500`. | | `pageCursor` | string | — | The cursor returned in the previous response's `next` field. Pass this value to retrieve the next page. | Pagination is cursor-based. When more results exist, the response includes a non-null `next` value. The response does not include a total count. *** ## Response ### Top-level shape ```json theme={"system"} { "data": [ /* audit log events */ ], "next": "eyJjdXJzb3IiOiAi..." } ``` `next` is `null` when no further pages exist. > [!NOTE] > > For backward compatibility, the response also includes a deprecated `cursor` field with the same value as `next`. New integrations should read `next`. *** ### Per-event fields | Field | Type | Description | | :- | :- | :- | | `id` | string | Unique identifier for the event. | | `timestamp` | ISO 8601 | The time in which the event occurred. | | `category` | string | The event category. | | `subject` | string | The event subject. | | `event` | string | The event type. | | `userId` | string | The ID of the user who triggered the event. | | `tenantId` | string | The workspace tenant ID. | *** ## Filter values All filter values are drawn from a controlled vocabulary maintained by Fireblocks. Filtering against values not in the vocabulary returns an empty result set. ### `category` (15 values) `Administration` · `Whitelist` · `Wallets` · `Exchanges` · `Settlements` · `Policies` · `Compliance` · `Transactions` · `Web3` · `Developers` · `Assets` · `Keys` · `Automation` · `Orders` · `Security` ### `subject` (61 values) Subjects describe the kind of entity an event acted on. Common values include: `Transaction`, `Policy`, `Vault`, `Wallet`, `User`, `Sign in`, `Webhook endpoint`, and `Report`. See [Audit Log Events](/reference/audit-log-events) for the full list. ### `event` (\~200 values) Events describe the action taken — for example, `Submitted`, `Completed`, `Failed`, `Created`, `Approved`, `Changed password`. Event values are scoped to a subject; the same event name can appear under multiple subjects. Filter by `category` or `subject` first to narrow the relevant set. See [Audit Log Events](/reference/audit-log-events) for the full list. *** ## Examples ### Filter by multiple categories within a date range ```text theme={"system"} GET /v1/management/audit_logs ?category=Administration &category=Security &startTime=1735689600000 &endTime=1738367999000 ``` Returns events in the `Administration` or `Security` category between January 1 and January 31, 2026. *** ### Query everything a specific user did in a date range ```text theme={"system"} GET /v1/management/audit_logs ?userId=abc123 &startTime=1735689600000 &endTime=1738367999000 ``` *** ### Filter by category, subject, and event together ```text theme={"system"} GET /v1/management/audit_logs ?category=Transactions &subject=Outgoing%20transaction &event=Completed &startTime=1735689600000 ``` Returns only completed outgoing transactions. *** ### Paginate through results **First request:** ```text theme={"system"} GET /v1/management/audit_logs?category=Security&pageSize=100&order=DESC ``` **Response:** ```json theme={"system"} { "data": [ /* ... */ ], "next": "eyJjdXJzb3IiOiAi..." } ``` **Next page:** ```text theme={"system"} GET /v1/management/audit_logs?category=Security&pageSize=100&order=DESC&pageCursor=eyJjdXJzb3IiOiAi... ``` When `next` is `null`, you have retrieved all results. *** ## Errors | Status | When | | :- | :- | | `400 Bad Request` | `startTime` or `endTime` is not a finite integer; `endTime` is not strictly greater than `startTime`; `order` is not exactly `ASC` or `DESC`; `pageSize` is not an integer between `1` and `500`. | | `403 Forbidden` | The caller's role is not permitted to access audit logs. | Error responses include a machine-readable code and a human-readable message: ```json theme={"system"} { "message": "Invalid pageSize. Must be between 1 and 500.", "code": 2903 } ``` *** ## Backward compatibility | Legacy parameter | Status | Use instead | | :- | :- | :- | | `timePeriod` (values: `DAY`, `WEEK`) | Deprecated. Ignored when `startTime` is provided. Defaults to `DAY` when neither is supplied. | `startTime` / `endTime` | | `cursor` | Deprecated alias of `pageCursor`. If both are sent, `pageCursor` wins. | `pageCursor` | # Binance Travel Rule Source: https://developers.fireblocks.com/docs/binance-travel-rule The Travel Rule `piiData` fields Binance requires for withdrawals and deposits, by jurisdiction, relationship, and counterparty wallet, with examples. Binance requires Travel Rule `piiData` for withdrawals and deposits. Its requirements depend on the jurisdiction of your Binance entity, and on whether the counterparty wallet is private or at another VASP. For how to build, encrypt, and send the payload, see [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule). * **Withdrawals:** send your jurisdiction as `originatingVASP.vaspCountry`. When the destination is at another VASP, identify it in `beneficiaryVASP`. For a private wallet, omit `beneficiaryVASP`. * **Deposits:** send your jurisdiction as `beneficiaryVASP.vaspCountry`. When the funds come from another VASP, identify it with `originatingVASP.vaspName`. For a private wallet, omit `originatingVASP`. Supported jurisdictions: Australia (`AU`), Bahrain (`BH`), France (`FR`), India (`IN`), Japan (`JP`), Kazakhstan (`KZ`), New Zealand (`NZ`, withdrawals only), Poland (`PL`), South Africa (`ZA`), and the United Arab Emirates (`AE`). ## Withdrawals | Path | Values | Required when | | - | - | - | | `data.originatingVASP.vaspCountry` | `AU`, `BH`, `FR`, `IN`, `JP`, `KZ`, `NZ`, `PL`, `ZA`, `AE` | Always | | `data.beneficiary.participantRelationshipType` | `FirstParty` (to yourself), `ThirdParty` (to another beneficiary) | Always | | `data.beneficiary.entityType` | `Individual`, `Business` | Always | | `data.beneficiary.names[0].primaryName`, `.nameType` | Free text; `Latin` or `Kana` | `Individual`, outside JP. JP and `ThirdParty`: holds the Kanji name | | `data.beneficiary.names[1].primaryName`, `.nameType` | Free text; `Kana` | JP and `ThirdParty` | | `data.beneficiary.names[2].primaryName`, `.nameType` | Free text; `Latin` | JP and `ThirdParty` | | `data.beneficiary.names[0].secondaryName` | Free text (last name) | `Individual`, outside JP | | `data.beneficiary.names[2].secondaryName` | Free text (Latin last name) | JP and `ThirdParty` | | `data.beneficiary.postalAddress.country` | ISO 2-letter code | JP or KZ; elsewhere, `ThirdParty` | | `data.beneficiary.postalAddress.city` | Free text | JP or KZ; `ThirdParty` in AU, BH, IN, or AE | | `data.beneficiary.company.name` | Free text | `ThirdParty` and `Business` | | `data.beneficiaryVASP.vaspCode` | Outside JP: `BINANCE`, `other`. JP: `BINANCE`, `OKCJJPTK` | Sending to another VASP, exchange, or hosted wallet | | `data.beneficiaryVASP.vaspName` | Outside JP: free text. JP: ISO 2-letter code | Outside JP, when `vaspCode` is `others`. Always in JP | | `data.beneficiaryVASP.vaspRegion` | Free text | JP, `vaspCode` is `other`, and the VASP is in China or Ukraine | | `data.transactionData.withdraw.isAddressVerified` | `true` | AU, FR, JP, KZ, NZ, PL, or ZA | | `data.transactionData.withdraw.txPurpose` | JP: `goods`, `inheritance`, `crossBorder`, `investment`, `service`, `loan`, `gifts`. KZ: `service`, `goods`, `p2p`, `charity`, or free text | JP or KZ | "Outside JP" means AU, BH, FR, IN, KZ, NZ, PL, ZA, and AE. ## Deposits | Path | Values | Required when | | - | - | - | | `data.beneficiaryVASP.vaspCountry` | `AU`, `BH`, `FR`, `IN`, `JP`, `KZ`, `PL`, `ZA`, `AE` | Always | | `data.originator.participantRelationshipType` | `FirstParty`, `ThirdParty` | Always | | `data.originator.entityType` | `Individual`, `Business` | Always | | `data.originator.names[0].primaryName`, `.nameType` | Free text; `Latin` or `Kana` | `Individual`, outside JP. JP and `ThirdParty`: holds the Kanji name | | `data.originator.names[1].primaryName`, `.nameType` | Free text; `Kana` | JP and `ThirdParty` | | `data.originator.names[2].primaryName`, `.nameType` | Free text; `Latin` | JP and `ThirdParty` | | `data.originator.names[0].secondaryName` | Free text (last name) | `Individual` in AU, BH, FR, IN, KZ, NZ, PL, ZA, or AE | | `data.originator.names[2].secondaryName` | Free text (Latin last name) | JP and `ThirdParty` | | `data.originator.company.name` | Free text | `ThirdParty` and `Business` | | `data.originator.postalAddress.country` | ISO 2-letter code | JP or KZ; elsewhere, `ThirdParty` | | `data.originator.postalAddress.city` | Free text | JP or KZ; `ThirdParty` in AU, BH, IN, or AE | | `data.originator.nationalIdentification.nationalIdentifierType` | `Individual`: `PASSPORT`, `NATIONAL_ID`. `Business`: `ABN`, `ACN`, `OTHER` | AU | | `data.originator.nationalIdentification.nationalIdentifier` | Free text | AU; IN and `Individual` | | `data.originator.registrationNumber` | Free text (PAN) | IN and `Business` | | `data.originator.postalAddress.subdivision` | Free text (state) | IN and `ThirdParty` | | `data.originator.postalAddress.postalCode` | Free text (PIN code) | IN and `ThirdParty` | | `data.originator.postalAddress.streetName` | Free text (address) | IN and `ThirdParty` | | `data.originator.postalAddress.region` | Free text | JP, and the originator is in China or Ukraine | | `data.originatingVASP.vaspName` | `BINANCE`, `others`, or free text | AU, BH, FR, IN, JP, PL, ZA, or AE | | `data.transactionData.deposit.isAddressVerified` | `true` | AU, FR, JP, PL, or ZA | | `data.transactionData.deposit.txPurpose` | `service`, `goods`, `p2p`, `charity`, or free text | KZ | ## Japanese names In Japan, names use three scripts, for businesses as well as individuals: ```json theme={"system"} "names": [ { "primaryName": "Kanji name", "nameType": "Kanji" }, { "primaryName": "Kana name", "nameType": "Kana" }, { "primaryName": "John", "secondaryName": "Doe", "nameType": "Latin" } ] ``` ## Examples ### First-party withdrawal to a private wallet (France) ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": { "participantRelationshipType": "FirstParty", "entityType": "Individual" }, "transactionData": { "withdraw": { "isAddressVerified": true } }, "originatingVASP": { "vaspCountry": "FR" } } } ``` ### Third-party withdrawal to another VASP (France) A withdrawal from a Binance account, by an individual in France, to a third-party beneficiary. Before encryption: ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": { "participantRelationshipType": "ThirdParty", "entityType": "Individual", "names": [{ "primaryName": "John", "nameType": "Latin", "secondaryName": "Doe" }], "postalAddress": { "country": "BE" } }, "beneficiaryVASP": { "vaspCode": "BINANCE" }, "transactionData": { "withdraw": { "isAddressVerified": true } }, "originatingVASP": { "vaspCountry": "FR" } } } ``` After encrypting every value in `data`, add it to the transaction: ```ts theme={"system"} const { data: tx } = await fireblocks.transactions.createTransaction({ transactionRequest: { assetId: "BTC", amount: "0.1", source: { type: "EXCHANGE_ACCOUNT", id: exchangeAccountId }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: beneficiaryAddress } }, extraParameters: { piiData: encryptedPiiData, }, }, }); ``` ### Third-party withdrawal to another VASP (Australia) ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": { "participantRelationshipType": "ThirdParty", "entityType": "Business", "company": { "name": "ACME Inc" }, "postalAddress": { "country": "US", "city": "Boston" } }, "beneficiaryVASP": { "vaspCode": "BINANCE" }, "transactionData": { "withdraw": { "isAddressVerified": true } }, "originatingVASP": { "vaspCountry": "AU" } } } ``` ### Third-party deposit from a private wallet (India) ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "originator": { "participantRelationshipType": "ThirdParty", "entityType": "Individual", "names": [{ "primaryName": "John", "secondaryName": "Doe", "nameType": "Latin" }], "nationalIdentification": { "nationalIdentifier": "12345xyz" }, "postalAddress": { "streetName": "Oak street 123", "city": "Buenos Aires", "postalCode": "123123123", "subdivision": "State", "country": "AR" } }, "beneficiaryVASP": { "vaspCountry": "IN" } } } ``` ## On the Help Center To provide Binance Travel Rule data in the Console, see [Binance travel rule](https://support.fireblocks.io/hc/en-us/articles/30749907293212-Binance-travel-rule). # Bitfinex Travel Rule Source: https://developers.fireblocks.com/docs/bitfinex-travel-rule The Travel Rule `piiData` fields Bitfinex requires for withdrawals, and how to identify the counterparty VASP. Bitfinex requires Travel Rule `piiData` for withdrawals only. For deposits, the sending VASP sends the originator data to Bitfinex directly. For how to build, encrypt, and send the payload, see [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule). ## Required fields for withdrawals | Path | Values | Required when | | - | - | - | | `data.beneficiary.participantRelationshipType` | `FirstParty`, `ThirdParty` | Always | | `data.beneficiary.entityType` | `Individual`, `Business` | Always | | `data.beneficiary.names[0].primaryName` | Free text, with `nameType` `Latin` | `Individual` | | `data.beneficiary.names[0].secondaryName` | Free text | `Individual` | | `data.beneficiary.company.name` | Free text | `Business` | | `data.beneficiary.postalAddress.streetName` | Free text | Always | | `data.beneficiary.postalAddress.buildingNumber` | Free text | Always | | `data.beneficiary.postalAddress.city` | Free text | Always | | `data.beneficiary.postalAddress.postalCode` | Free text | Always | | `data.beneficiary.postalAddress.country` | Free text | Always | | `data.beneficiaryVASP.vaspCode` | Value from the Bitfinex VASP list | Always | ## Identify the counterparty VASP Bitfinex publishes the full list of VASPs it supports through its [VASP list endpoint](https://docs.bitfinex.com/reference/virtual-asset-service-providers). ## On the Help Center To provide Bitfinex Travel Rule data in the Console, see [Bitfinex travel rule](https://support.fireblocks.io/hc/en-us/articles/30750103946140-Bitfinex-travel-rule). # Bitstamp Travel Rule Source: https://developers.fireblocks.com/docs/bitstamp-travel-rule The Travel Rule `piiData` fields Bitstamp requires for withdrawals and deposits, first party and third party, with an example. Bitstamp requires Travel Rule `piiData` for withdrawals and deposits. Its requirements do not depend on jurisdiction: the same fields apply to withdrawals and deposits, first party and third party. For how to build, encrypt, and send the payload, see [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule). ## Required fields In each path, `{party}` is `beneficiary` for withdrawals and `originator` for deposits. | Path | Values | Required when | | - | - | - | | `data.{party}.participantRelationshipType` | `FirstParty`, `ThirdParty` | Always | | `data.{party}.entityType` | `Individual`, `Business` | Always | | `data.{party}.names[0].primaryName` | Free text, with `nameType` `Latin` | `Individual` | | `data.{party}.names[0].secondaryName` | Free text | `Individual` | | `data.{party}.dateOfBirth` | Free text | `Individual` | | `data.{party}.company.name` | Free text | `Business` | | `data.{party}.postalAddress.streetName` | Free text | Always | | `data.{party}.postalAddress.buildingNumber` | Free text | Always | | `data.{party}.postalAddress.city` | Free text | Always | | `data.{party}.postalAddress.postalCode` | Free text | Always | | `data.{party}.postalAddress.country` | Free text | Always | | `data.beneficiaryVASP.vaspCode` (withdrawals), `data.originatingVASP.vaspCode` (deposits) | Value from the Bitstamp VASP list | Always | ## Identify the counterparty VASP Identify the counterparty VASP by its Bitstamp VASP ID, a UUID, in `beneficiaryVASP.vaspCode` for withdrawals or `originatingVASP.vaspCode` for deposits. Bitstamp publishes the full list of VASPs it supports through its [VASP list endpoint](https://www.bitstamp.net/api/#tag/Travel-rule-public/operation/VaspList). ## Example ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": { "participantRelationshipType": "ThirdParty", "entityType": "Individual", "names": [{ "primaryName": "John", "secondaryName": "Doe", "nameType": "Latin" }], "dateOfBirth": "2000-01-01", "postalAddress": { "streetName": "Oak street", "buildingNumber": "1", "city": "Boston", "postalCode": "02001", "country": "US" } }, "beneficiaryVASP": { "vaspCode": "914a9dae-4234-45d1-be83-fd40e818e381" } } } ``` ## On the Help Center To provide Bitstamp Travel Rule data in the Console, see [Bitstamp travel rule](https://support.fireblocks.io/hc/en-us/articles/30749966918556-Bitstamp-travel-rule). # Boost Transactions Source: https://developers.fireblocks.com/docs/boost-transactions > [!TIP] > > **Let ATC detect boost candidates automatically.** Account Traffic Control > (ATC) monitors your pending EVM transactions and fires a > `transaction.alert.stuck` webhook when a transaction needs a boost — including > the transaction ID to act on immediately. See [Account Traffic Control](https://support.fireblocks.io/hc/en-us/articles/27129539023004-Account-Traffic-Control). ETH transactions can get stuck due to insufficient gas fees or sudden spikes in network congestion. When the gas fee provided is too low compared to the current network demand, validators may prioritize other transactions with higher fees, causing your transaction to remain unprocessed in the mempool. This can lead to delays and a backlog of subsequent transactions, especially if they depend on the completion of the stuck transaction. Managing gas fees and monitoring network conditions are crucial to prevent transactions from getting stuck. Replace By Fee (RBF) and Child Pays For Parent (CPFP) are mechanisms used to expedite stuck transactions on blockchain networks. * **RBF** - commonly used in Ethereum and other EVM-based networks, allows a sender to replace a pending transaction with a new one that has a higher gas fee, increasing the likelihood of it being processed promptly. * **Drop EVM transaction** - uses RBF to replace the original transaction with a new transaction of 0 value, and a destination identical to the source, effectively canceling the transfer. * **CPFP** - primarily used in Bitcoin, involves creating a new transaction (the child) that pays a higher fee, incentivizing miners to also confirm the original stuck transaction (the parent) due to the combined higher fee. Both RBF and CPFP methods aim to prioritize the processing of transactions that might otherwise remain stuck due to low fees or network congestion. # RBF and CPFP in Fireblocks Fireblocks customers can boost or drop their stuck transactions using the following methods: * Clicking the "Boost" icon on the Transaction card in the Fireblocks Web Console. For more information on the process and the availability of the boosting operation, refer to this [guide](https://support.fireblocks.io/hc/en-us/articles/360018600160-Boost-or-drop-a-stuck-EVM-based-transaction). * Using the Fireblocks API for the following scenarios: * [Perform RBF for a stuck EVM transaction](/reference/boost-transactions#replace-by-fee-rbf-for-evm-networks) * [Drop a stuck EVM transaction](/reference/boost-transactions#drop-evm-transaction) * [Perform CPFP for a stuck BTC transaction](/reference/boost-transactions#cpfp-child-pays-for-parent-for-btc) # Building with AI Source: https://developers.fireblocks.com/docs/building-with-ai AI-assisted development is the default way to build on Fireblocks. This page explains the tools available and when to use each. Building on Fireblocks is designed to work with AI agents. Start by connecting your agent to the docs — the Documentation MCP is the one step that applies regardless of how you build. From there, use whichever combination of SDKs, CLI, and platform tools fits your workflow. > **[Get Started](/docs/quickstart)** > > The quickstart walks you through connecting your agent to the docs, setting up API authentication, and making your first Fireblocks API call — with SDK and CLI paths both covered. ## Developer tools These are the tools that matter most when you're writing code against the Fireblocks API. ### Fireblocks Documentation MCP **What it is:** An MCP server that gives your coding agent (Cursor, Claude Code, Codex, or any MCP-compatible tool) real-time access to Fireblocks developer documentation. Your agent can search, read, and cross-reference docs without leaving your IDE. **When to use it:** Always. Install this before anything else. It keeps your agent grounded in canonical, up-to-date documentation rather than training-data guesses — especially important for Fireblocks, where API behavior, authentication, and object models have specific details that general LLM training may not reflect accurately. To add the server yourself instead, follow the steps for your tool in [Getting Started](/docs/quickstart#step-0-connect-your-ai-agent-to-fireblocks-docs), or point any MCP-compatible client at `https://developers.fireblocks.com/mcp`. Once installed, your agent can answer questions like: * *What fields does a vault account have?* * *How does Fireblocks JWT signing work?* * *What webhooks fire when a transaction is confirmed?* **Without an MCP client:** Every page on this site has a **Copy Page** button — use it to paste a page directly into Claude, ChatGPT, or any other LLM. You can also open the page URL in Claude's Projects or any tool with web access. The MCP handles search and freshness automatically, but copying a specific page works well when you need focused context on a single topic. ### llms.txt and llms-full.txt Fireblocks publishes documentation in the [llms.txt](https://llmstxt.org/) format for tools and workflows that expect a static, crawlable text bundle: * **[llms.txt](https://developers.fireblocks.com/llms.txt)** — a compact index and entry point to the docs. * **[llms-full.txt](https://developers.fireblocks.com/llms-full.txt)** — a broader aggregate of documentation content for setups that load one large file. Use these when you cannot run an MCP client, need an offline or snapshot ingest for a custom index, or are wiring a tool that only understands llms.txt URLs. For everyday coding with an AI agent, the **Fireblocks Documentation MCP** (above) is the best option: it searches and retrieves the right pages on demand, reflects the current site, and avoids pulling huge static exports into context. Treat `llms.txt` / `llms-full.txt` as a fallback or supplement, not a replacement for the docs MCP. *** ### Fireblocks CLI **What it is:** An agent-first command-line tool that exposes every Fireblocks API operation as a typed, JWT-signed command. The `help-index` command returns a compact JSON index of all commands sized to fit in an LLM context window. **When to use it:** When you want to explore your workspace, prototype API calls, run scripts or CI jobs, or let your agent propose exact commands you can review before running. The CLI is the fastest path from "I want to do X" to a working, reviewable command — use it alongside an SDK in production, or on its own for operator and agent workflows. **How to use it:** To have your agent install and verify the CLI for you, copy the agent prompt in [Fireblocks Documentation MCP](#fireblocks-documentation-mcp) and choose the CLI when your agent asks. You enter your credentials in your own terminal, so your agent never sees your keys. **npm** ```bash theme={"system"} npm install -g @fireblocks/fireblocks-cli ``` **Homebrew (macOS)** ```bash theme={"system"} brew tap fireblocks/fireblocks-cli brew install fireblocks-cli ``` **Standalone / other** Download a pre-built binary from [GitHub Releases](https://github.com/fireblocks/fireblocks-cli/releases), or install via yarn/pnpm. See [Fireblocks CLI](/docs/fireblocks-cli) for all options. Connect it to your workspace, then verify: ```bash theme={"system"} fireblocks configure fireblocks whoami ``` Key agent-friendly features: * `fireblocks help-index` — compact JSON command catalogue for LLM context * `--dry-run` — preview a request before it executes * `--no-confirm` — skip interactive prompts in automated pipelines * Structured JSON errors on stderr with distinct exit codes > [!TIP] > > For any operation that creates or modifies data, ask your agent to show the `fireblocks` command first and use `--dry-run` where available. Review before running. Full reference: [Fireblocks CLI](/docs/fireblocks-cli) *** ## Platform AI tools These tools extend AI capabilities to the Fireblocks Console and broader operations workflows. ### Fireblocks AI Link **What it is:** An MCP server that connects your AI tools (Claude, ChatGPT, Cursor) directly to your Fireblocks workspace data. Where the Documentation MCP gives your agent access to docs, AI Link gives it access to your live Fireblocks environment — balances, transactions, addresses, and workspace state. **When to use it:** When you want to query live workspace data in natural language, build dashboards, or run operational queries without writing API code. AI Link is oriented towards operations and treasury workflows rather than code generation. **Two deployment modes:** | Mode | Capabilities | Deployment | Best for | | - | - | - | - | | **Remote MCP** | Read-only | Fireblocks-hosted | Connecting to ChatGPT, Claude, or other external AI platforms | | **Local MCP** | Read + write (transactions) | Self-hosted, open-source | Custom workflows with full data control | Example queries AI Link enables: * *What percentage of my holdings are in stablecoins?* * *Summarize fees paid across all vaults this month.* * *What is the current balance of vault account 42?* Available via [Fireblocks Labs](https://support.fireblocks.io/hc/en-us/articles/22235997516444-Fireblocks-Labs-Early-Access-Feature-Hub) for early access. The Local MCP server is open-source on GitHub. *** ### Fireblocks Genie **What it is:** A native AI assistant built directly into the Fireblocks Console — no external integrations required. Genie answers treasury and operations questions in real time using your workspace data, and can explain complex DeFi contract calls in plain language via the AI Transaction Summary feature. **When to use it:** When you're in the Console and want fast answers about your workspace state, holdings, or a specific transaction — without switching context to another tool. Genie is not a developer tool; it's built for treasury, finance, and operations teams who work in the Console. **Capabilities:** * Answer natural-language questions about balances, fees, and holdings * AI Transaction Summary: explains complex smart contract calls in human-readable terms * Respects your workspace policies, approval quorums, and user roles Available via [Fireblocks Labs](https://support.fireblocks.io/hc/en-us/articles/22235997516444-Fireblocks-Labs-Early-Access-Feature-Hub) for early access. *** ## How the tools fit together | Tool | Primary user | Primary context | Needs live workspace data | | - | - | - | - | | **Documentation MCP** | Developer / agent | IDE / coding agent | No — reads docs only | | **Fireblocks CLI** | Developer / agent | Terminal / scripts | Yes — calls the API | | **AI Link** | Developer / operator | External AI tools | Yes — reads/writes workspace | | **Genie** | Operations / treasury | Fireblocks Console | Yes — console-native | The **Documentation MCP** is the one tool that applies to every workflow — install it first. After that, use the SDK, CLI, or both depending on what you're building. Add AI Link when you want natural-language access to live workspace data from your own AI tools. Genie is available in the Console for non-developer users on your team. # Canton Overview Source: https://developers.fireblocks.com/docs/canton Fireblocks support for the Canton network: reading Canton items, receiving webhook notifications, and writing calls and offer responses. Canton items — onboarding, allocations, and transfers, including DTCC and Tradeweb flows — are ordinary Fireblocks transactions. There is no separate Canton resource tree: everything Canton-specific arrives in a `cantonDetails` object on the transaction, and the same object is what you act on and what you receive. This section covers the three things a Canton integration needs: * **[Read Canton Transactions](/docs/read-canton-transactions)** — list and read Canton activity with the transactions endpoints you already use, and identify what a `cantonDetails` object is telling you. * **[Canton Transaction Webhooks](/docs/canton-transaction-webhooks)** — the push side. An inbound offer is a status update on a transaction you never created, so a Canton integration is a webhook consumer first. * **[Write Canton Transactions](/docs/write-canton-transactions)** — make a Canton call to act on your own wallet or a contract, or answer an offer to respond to one. > [!NOTE] > > Start with the webhook. Most Canton activity — an inbound onboarding offer, an allocation request — begins as a notification, not as something you polled for. # Canton Transaction Webhooks Source: https://developers.fireblocks.com/docs/canton-transaction-webhooks Canton transactions notify you through the standard TRANSACTION_STATUS_UPDATED webhook. Branch on availableResponses to find offers you can answer. A Canton integration is a webhook consumer first. An inbound offer is a status update on a transaction you never created, so there is nothing to poll for until the webhook fires. Fireblocks delivers these to the endpoint you register. You implement the receiver. The envelope is the standard Fireblocks webhook envelope, not a Canton-specific one, and the `data` object is the standard transaction notification. The only Canton-specific thing on the wire is the `cantonDetails` key on `data`. ## Which event fires Only `TRANSACTION_STATUS_UPDATED` carries `cantonDetails`. A Canton integration can discard every other event type at the front door. | Field | Type | Description | | - | - | - | | `type` | string | Always `TRANSACTION_STATUS_UPDATED`. | | `tenantId` | uuid | The Fireblocks workspace the notification belongs to. Verify it against your own workspace ID rather than trusting the delivery URL. | | `timestamp` | int64 | When Fireblocks emitted the notification, in epoch milliseconds. Not a sequence number. | | `data` | object | The transaction as of this notification. | ## Route an incoming notification ```text theme={"system"} 1. type == TRANSACTION_STATUS_UPDATED 2. read data.cantonDetails 3. no cantonDetails → not a Canton transaction, ignore 4. cantonDetails.call → an outgoing call of yours progressed 5. cantonDetails.settlement → a venue settled an allocation you had locked 6. cantonDetails.offerResponse → offer conversation: availableResponses non-empty → answer it POST /v1/operations/canton/offers/{data.id}/responses availableResponses empty → do not post; follow originalTransactionId if set ``` To answer in step 6, call [Answer an offer](/api-reference/canton-beta/answer-an-offer) with `data.id` as `offerId`. Step 4 covers calls you submitted with [Make a Canton call](/api-reference/canton-beta/make-a-canton-call). Both are covered end to end in [Write Canton Transactions](/docs/write-canton-transactions). > [!WARNING] > > Step 6 is the answerability rule. Branch on `availableResponses`, never on `subStatus`. Examples 1 and 4 below share `status: CONFIRMING` and `subStatus: PENDING_RECEIVER_APPROVAL`, and only one of them is answerable. ## The five states to tell apart **1. Actionable offer** An inbound DTCC end-investor onboarding offer. `availableResponses` carries both DTCC values, so this transaction ID is the one to answer. ```json theme={"system"} { "type": "TRANSACTION_STATUS_UPDATED", "tenantId": "d41b6f28-9c07-4b13-8e5a-72f0c4a91e36", "timestamp": 1788091200000, "data": { "id": "3f9c1e77-a4b2-4d10-9e88-6c0f1a7b3d52", "operation": "CANTON_CALL", "status": "CONFIRMING", "subStatus": "PENDING_RECEIVER_APPROVAL", "assetId": "CANTON", "amount": "0", "source": { "type": "EXTERNAL" }, "destination": { "type": "VAULT_ACCOUNT", "id": "12" }, "cantonDetails": { "offerResponse": { "version": 1, "domain": "ONBOARDING", "vendor": "DTCC", "availableResponses": [ "DTCC_END_INVESTOR_ONBOARDING_ACCEPT", "DTCC_END_INVESTOR_ONBOARDING_REJECT" ] } } } } ``` **2. Response in flight** The same transaction as example 1, after a response was dispatched. `availableResponses` has emptied and `approvalTransactionId` names the outgoing response transaction. Posting again here returns `409 response-not-available`. This is a genuine state, not a race. The offer is neither answerable nor resolved until the response transaction confirms. ```json theme={"system"} { "type": "TRANSACTION_STATUS_UPDATED", "tenantId": "d41b6f28-9c07-4b13-8e5a-72f0c4a91e36", "timestamp": 1788091260000, "data": { "id": "3f9c1e77-a4b2-4d10-9e88-6c0f1a7b3d52", "operation": "CANTON_CALL", "status": "CONFIRMING", "subStatus": "APPROVING", "assetId": "CANTON", "amount": "0", "source": { "type": "EXTERNAL" }, "destination": { "type": "VAULT_ACCOUNT", "id": "12" }, "cantonDetails": { "offerResponse": { "version": 1, "domain": "ONBOARDING", "vendor": "DTCC", "availableResponses": [], "approvalTransactionId": "5a1d8b0f-2c31-4c7e-9d0a-118e7b6c4a21" } } } } ``` **3. Allocation locked** The outgoing offer-response transaction created by accepting an allocation, now locked on chain. This `data.id` is what `ALLOCATION_WITHDRAW` takes as `allocationTransactionId`, and this status pair is the only state in which it is accepted. The transaction stays here until the venue settles it, moving to `COMPLETED`, or a withdraw succeeds, moving it to `CANCELLED`. ```json theme={"system"} { "type": "TRANSACTION_STATUS_UPDATED", "tenantId": "d41b6f28-9c07-4b13-8e5a-72f0c4a91e36", "timestamp": 1788094800000, "data": { "id": "9f2b7c14-8d55-4a02-b6e1-5c3f90a7d488", "operation": "CANTON_CALL", "status": "CONFIRMING", "subStatus": "ALLOCATED", "assetId": "CANTON", "amount": "1000", "source": { "type": "VAULT_ACCOUNT", "id": "12" }, "destination": { "type": "EXTERNAL" }, "cantonDetails": { "offerResponse": { "version": 1, "domain": "ALLOCATIONS", "availableResponses": [], "originalTransactionId": "c9e05a37-4b81-4f22-9d68-1a73e8b0c542", "verdict": "ACCEPTED" } } } } ``` **4. Linked leg** The vault-to-one-time-address transfer case, and the reason the answerability rule exists. One transfer offer flags two transactions with `PENDING_RECEIVER_APPROVAL`. This is the other one. It matches example 1 on every field you are tempted to filter on, including `status`, `subStatus`, and `operation`. It differs only in `availableResponses` being empty and `originalTransactionId` being set. Follow that pointer to find where the answer goes. This transaction is not broken, not a duplicate, and must not be ignored. It is half of the offer's ledger footprint. It simply is not where the answer goes. ```json theme={"system"} { "type": "TRANSACTION_STATUS_UPDATED", "tenantId": "d41b6f28-9c07-4b13-8e5a-72f0c4a91e36", "timestamp": 1788098400000, "data": { "id": "b8f30d51-6a27-4c94-8e15-9d03f7a2c168", "operation": "CANTON_CALL", "status": "CONFIRMING", "subStatus": "PENDING_RECEIVER_APPROVAL", "assetId": "CANTON", "amount": "250", "source": { "type": "VAULT_ACCOUNT", "id": "12" }, "destination": { "type": "ONE_TIME_ADDRESS" }, "cantonDetails": { "offerResponse": { "version": 1, "domain": "TRANSFER", "availableResponses": [], "originalTransactionId": "7c4a2f81-3d95-4e60-b1a7-8f24c6d0e913" } } } } ``` **5. Settlement receipt** The venue settled the allocation from example 3, and this is the credit side: a real Canton transaction, already `COMPLETED` when it is created, carrying the asset and amount you receive. There is nothing to answer and you did not initiate it, so it carries neither `offerResponse` nor `call`. Its `settlement.originalTransactionId` names the allocation it pays for. Without that pointer, this is an unexplained incoming credit. ```json theme={"system"} { "type": "TRANSACTION_STATUS_UPDATED", "tenantId": "d41b6f28-9c07-4b13-8e5a-72f0c4a91e36", "timestamp": 1788102000000, "data": { "id": "e2704b96-1f83-4d57-a0c9-6b58e3d1f204", "operation": "CANTON_CALL", "status": "COMPLETED", "assetId": "Amulet", "amount": "500", "source": { "type": "EXTERNAL" }, "destination": { "type": "VAULT_ACCOUNT", "id": "12" }, "cantonDetails": { "settlement": { "version": 1, "domain": "ALLOCATIONS", "type": "ALLOCATION_SETTLEMENT", "originalTransactionId": "9f2b7c14-8d55-4a02-b6e1-5c3f90a7d488" } } } } ``` ## Sub-status reference | Sub-status | New | What it means | | - | - | - | | `PENDING_RECEIVER_APPROVAL` | No | An offer is outstanding. Not a reliable answerability signal. | | `APPROVING` | Yes | A response is in flight on this offer. `availableResponses` is empty while it is, and a second post returns `409`. | | `ALLOCATED` | Yes | An allocation is locked on chain and withdrawable. The transaction stays at `CONFIRMING`. | | `CONFIRMED` | No | The counterparty accepted. | | `REJECTED_BY_RECEIVER` | No | The counterparty rejected. | | `RECEIVER_APPROVAL_TIMEOUT` | No | The offer expired unanswered. | Note that `CONFIRMING` is not transient for Canton. An accepted allocation sits at `CONFIRMING` with `ALLOCATED` until the venue settles it. ## Delivery semantics Delivery is at-least-once and unordered. Two notifications for the same transaction can arrive out of timestamp order, and `timestamp` is not a sequence number. Treat every notification as a state snapshot rather than a transition. There is no `previousStatus` field, by design. Re-read `availableResponses` on each notification instead of tracking transitions yourself, and make handling idempotent on `data.id` plus `data.status` plus `data.subStatus`. Parse permissively. Both the envelope and `data` carry fields beyond this contract, and a strict parser breaks on the next unrelated platform addition. ## What your endpoint should return | Status | Effect | | - | - | | `200` | Acknowledged. Fireblocks does not redeliver. | | `4XX` | Rejected. Fireblocks redelivers with backoff. | | `5XX` | Receiver error. Fireblocks redelivers with backoff. | > [!TIP] > > Acknowledge first and process asynchronously. Holding the connection open while a handler runs risks a timeout, which is indistinguishable from a failure and triggers a redelivery. Return `5XX` for a transient failure, because redelivery is what you want. Do not return `4XX` for a payload you will never accept, such as one with no `cantonDetails`, because that becomes a retry loop. Return `200` and drop it instead. ## Reconciling without webhooks The webhook is the push half of the read side. [`GET /v1/transactions/{txId}`](/api-reference/transactions/get-a-specific-transaction-by-fireblocks-transaction-id) is the pull half, and both carry the identical top-level `cantonDetails` object. Use the pull path at startup, after downtime, or when a delivery fails. See [Read Canton Transactions](/docs/read-canton-transactions). # Fireblocks Key Features & Capabilities Source: https://developers.fireblocks.com/docs/capabilities ### Hot, warm, and cold MPC-CMP wallets Fireblocks wallets can be hot, warm, or cold. What separates these types of wallets is where the third MPC key share is held, and how transaction approvals are conducted. * With a **hot wallet**, the third MPC key share is held by an API user on an API co-signer, and transaction approvals can be automated. * With a **warm wallet**, the third MPC key share is held on your internet-connected mobile device, and approvals occur on the Fireblocks mobile app. * With a **cold wallet**, the third MPC key share is held on your air-gapped (offline) mobile device. Approvals for transactions require bi-directional QR code scanning. > [!TIP] > > **Workspace compatibility:** A Fireblocks workspace can be hot & warm, or cold wallet-only, but not both. ### Workspaces The **Fireblocks workspace** is a unique feature of the Fireblocks platform with a broad range of capabilities that allows you to manage your various accounts, digital assets, transactions, and more. Each workspace is a unique BIP32-HD wallet structure with unique security and transaction policies. [Learn more about different types of Fireblocks workspace environments.](/docs/workspace-environments) ### Role-based access control Fireblocks has extensive role-based access control capabilities for various user roles. These access roles grant them permissions related to: * Parts of the platform they can access * Types of actions they can perform * MPC key shares that they hold and can use to sign transactions These roles can range from admin-level users like 'Owner' of the workspace, to a read-only 'Viewer’. An API user can be assigned any user role (except 'Owner'). [Learn more about each role's access and capabilities.](/docs/manage-users) ### Admin Quorum The **Admin Quorum**is the minimum number of workspace admins required to approve sensitive workspace changes, such as adding or removing users, adding whitelisted addresses, or approving network connections. This is set via an Admin Quorum threshold. If an admin attempts to perform malicious actions, such as attempting to steal funds via a personal wallet, the multiple approvals required by the admin quorum prevent and mitigate damage. This would work the same for an admin that has their account compromised. [Learn more about adjusting your workspace's Admin Quorum threshold.](https://support.fireblocks.io/hc/en-us/articles/29110816772380-Admin-quorum) ### Accounts **Accounts** compile all types of accounts that Fireblocks supports, including; vault accounts, exchange accounts, and fiat accounts. * A**Vault account** is a unique on-chain wallet, with your private key secured by our MPC-CMP architecture, that enables you to securely store and transfer your digital assets. * A **Exchange account** allows you to leverage your exchange's API credentials to securely transfer assets between exchanges and other Fireblocks accounts. * A **Fiat account** enables you to transfer fiat to any other account within your Fireblocks workspace or network connections that support that specific fiat provider. ### Vaults The Fireblocks Vault is your secure MPC-CMP solution for wallet and address management. The Vault allows you to create and manage multiple vault accounts, which contain your asset wallets. Depending on the asset type, you may or may not be able to have multiple deposit addresses or accounts within a single vault account. [Learn how to create vaults and wallets using the Fireblocks API.](/docs/create-direct-custody-wallets) ### Wallet Pools A *Wallet Pool* groups vault accounts under one logical source. Send from the pool and Fireblocks selects a member for each transaction, preferring members that are not congested, which removes the single hot vault account as a throughput limit. [Learn how to set up and send from a Wallet Pool.](/docs/wallet-pools) ### Assets Asset wallets are used to manage internal deposit addresses for different asset types. Each asset wallet contains at least one deposit address for its asset type. Fireblocks supports over 1,200 assets and our asset support is continuously growing. [Learn how to return a list of supported assets using the Fireblocks API.](/docs/list-supported-assets-1) ### Policies Policies are a set of rules that set the limits and boundaries of the transactions in your Fireblocks workspace. With Policies, you control who can move funds, how much can be transferred in a single transaction or a certain time period, and how transactions are authorized. Policy rules can be applied to virtually any parameter within a transaction, including smart contract-specific transactions such as deploying, upgrading, and performing ongoing operations. [Learn how to set up rules for your Policies.](/docs/set-transaction-authorization-policy) ### Tags Tags are labels you attach to vault accounts. *Standard tags* classify and filter your vault accounts. *Protected tags* can also be referenced in Policy rules, and every change to them requires quorum approval. [Learn how to create, attach, and filter by tags using the Fireblocks API.](/docs/tags) ### Transactions API All transactions are routed through the [Create a new transaction](/api-reference/transactions/create-a-new-transaction) API call. Users can only issue transactions based on their access roles and the workspace’s Policy settings. This includes both console and API users. When issuing a transaction through this endpoint, the `OPERATION` parameter specifies what type of transaction this may be. It could be a generic transfer, a token mint or burn, a contract call, a typed message, or a raw message. [Learn how to build a transaction using the Fireblocks API.](/transactions/create-transactions) ### Whitelisted addresses **Whitelisted addresses** are deposit addresses that exist outside of your Fireblocks Vault. You can perform transactions from your workspace by whitelisting an address for any supported blockchain. Whitelisted addresses (also called "wallets") can be categorized as: * **Internal wallet** - a deposit address existing inside your organization. * **External wallet** - a deposit address existing outside your organization. * **Contract wallet** - a deposit address of an on-chain smart contract. [Learn to whitelist new addresses for transactions and deposits.](/docs/whitelist-addresses) ### Fireblocks Network The Fireblocks Network is a settlement workflow allowing you to quickly transfer with your network counterparties without the need to manually whitelist their addresses. Streamline settlement using the Fireblocks Network by automatically authenticating addresses with your network counterparties while automatically rotating secure addresses for supported assets. It also maps transactions to counterparties for accurate reporting. [Learn how to create and manage network connections using the Fireblocks API.](/docs/connect-to-the-fireblocks-network) ### Fireblocks Key Link **Fireblocks Key Link** lets you integrate your organization's Key Management Infrastructure (such as an on-premises or cloud-based HSM) directly with the Fireblocks platform. You get access to Fireblocks' wallet management, governance engine, transfer network, and compliance capabilities while retaining full control over your key storage across hot, warm, and air-gapped cold environments. [Learn more about Fireblocks Key Link.](https://support.fireblocks.io/hc/en-us/articles/14228517105052-Fireblocks-Key-Link-Overview) ### Tokenization Deploy, manage, mint, and burn custom tokenized assets on-chain. Enforce governance and rules for who is able to perform sensitive operations such as minting new tokens. [Learn how to deploy, manage, mint, and burn tokenized assets using the Fireblocks API.](/docs/issue-new-tokens) ### Web3 / DeFi access Fireblocks lets you securely connect to and operate seamlessly within the Web3 and DeFi ecosystem. * Connect easily to Web3 dApps using our WalletConnect integration or browser extension. * Interact directly and programmatically with smart contracts using our smart contract API. [Learn how to connect to Web3 dApps using our WalletConnect integration.](https://support.fireblocks.io/hc/en-us/articles/5403817784732-Connecting-to-Web3-using-WalletConnect) ### Securely develop and operate smart contracts Securely develop, deploy, and operate your on-chain smart contracts using Fireblocks' industry-leading security layers. Provide granular role-based access control for managing which developer is allowed to deploy smart contracts, perform upgrades, or call sensitive smart contracts operations, such as pausing or updating contract data. [Learn how to deploy and operate smart contracts using the Fireblocks API.](/docs/interact-with-smart-contracts) ### Fireblocks Gas Station The *Fireblocks Gas Station* service automates gas replenishment for token transaction fees on EVM-based networks such as Ethereum, BNB Chain, and others, and on TRON. It fuels your vault accounts from a Gas Station tank, a vault account or Wallet Pool in your own workspace that holds the base assets. This eliminates monitoring and manually transferring funds to each vault account to cover future transaction fees. [Learn how to set up your Gas Station using the Fireblocks API.](/docs/work-with-gas-station) ### Gasless transactions With *gasless transactions*, a relay vault account pays the network fee, so vault accounts can send tokens without holding the chain's native asset. EVM Gasless (Universal Gasless) uses EIP-7702 on EVM chains, and Solana Gasless uses a fee payer for SPL tokens. [Learn how gasless transactions work.](/docs/gasless-transactions) ### Transaction screening (AML/KYT) *Transaction screening* allows you to automate real-time monitoring of your crypto transactions in order to ensure compliance with Anti-Money Laundering/Counter Financing of Terrorism (AML/CFT) regulations, prevent interactions with sanctioned entities and identify customer behavior. [Learn more about our AML screening engine and the KYT capabilities of our Chainalysis and Elliptic integrations.](/docs/define-aml-policies) # Chainalysis Source: https://developers.fireblocks.com/docs/chainalysis Connect Chainalysis for AML/KYT screening: what Fireblocks sends, the risk result returned, timeouts, and supported assets. Chainalysis is an AML/KYT provider. It is a premium add-on that Fireblocks Support enables on Testnet and Mainnet workspaces, and you connect it in the Console; there is no API for connecting. Through the API you read its policy and results the same way as for any AML/KYT provider; see [AML/KYT Overview](/docs/aml-kyt-overview). ## What is sent and returned Fireblocks sends asset and network, amount and USD value, destination address, transaction hash and source address (for incoming and sent transfers), destination tag, and the [Customer Reference ID](/docs/customer-reference-id). Chainalysis returns a categorical risk level and the risk category it matched, if any. In `GET /screening/transaction/{txId}`, the risk level is in the `risk` field as a provider-specific string, such as `severeRisk`, `highRisk`, `mediumRisk`, `lowRisk`, or `noRiskInfo`. Older transactions can carry legacy values such as `HIGH` or `MEDIUM`, so do not validate against a fixed list. `externalId` is the identifier Chainalysis assigned when Fireblocks registered the item. For an incoming transaction it identifies a transfer (`GET /v2/transfers/{externalId}`); for an outgoing transaction a withdrawal attempt (`GET /v2/withdrawal-attempts/{externalId}`), not a transfer. Use the same Chainalysis API key as your Fireblocks provider configuration. Outcome (Post-Screening Policy) actions: Accept, Reject, Alert. ## Connect You need a Chainalysis API key with Know Your Transaction (KYT) API access. Enter it in the Console; default Trigger (Screening Policy) and Outcome rules apply until you create your own. To switch to new credentials, submit a ticket to Fireblocks Support. ## Timeouts | Direction | Wait | Resolved by | | - | - | - | | Incoming | Up to 10 minutes | Transaction hash, after a number of blockchain confirmations | | Outgoing | Up to 1 minute | Address | If no result arrives in time, `bypassScreeningDuringServiceOutages` decides what happens; see [Outage and delay settings](/docs/compliance-policies#outage-and-delay-settings). ## Supported assets Chainalysis supports blockchains and tokens broadly, including Testnet assets. Newly deployed tokens are usually supported automatically. ## Limitations * An ERC-20 token needs a USD price in Fireblocks to be screened. Outgoing transactions in an unpriced token are not registered with Chainalysis, and incoming ones do not trigger risk alerts, so risk-based Outcome rules cannot apply. * SOL transactions with an amount of 0.0 bypass screening with an Unsupported Route status. * Rejection details are available only from Fireblocks, not from Chainalysis. ## On the Help Center To connect Chainalysis or test your rules in the Console, see [Chainalysis](https://support.fireblocks.io/hc/en-us/articles/30614362886428-Chainalysis). # Off-ramp with Circle Payment Network (CPN) Source: https://developers.fireblocks.com/docs/circle-payments-network-cpn-api-guide ## Overview Circle Payments Network (CPN) is an account-based off-ramp provider that converts USDC to fiat through smart contract settlement. This guide covers the full API integration: discovering requirements, quote, order creation, smart contract settlement signing, tracking, and the Requirements (RFI) flow. The examples use USDC → BRL via PIX as a concrete reference. The CPN integration supports all assets and rails that CPN itself supports: USDC across the blockchains CPN settles on, and CPN's full set of fiat currencies and payment rails. For another corridor, substitute the appropriate `baseAssetId` (USDC on your chain), `quoteAssetId`, `quoteAssetRail`, and whitelisted destination; the flow and request structures are identical. ## Requirements ### Fireblocks workspace * A Fireblocks workspace with a connected and approved CPN account. * A vault account holding USDC on the blockchain you want to settle from (for example, Ethereum, Polygon, or Solana). * The native blockchain asset in the vault to cover the gas fee on the settlement transaction (e.g. ETH, MATIC, or SOL). * A whitelisted external wallet for the fiat destination. The wallet must have status: `"APPROVED"` before it can be used in an order. ### API user Create a Fireblocks API user in the Console, download the `.pem` private key, and note the API key UUID. Every request must be signed as a JWT using RS256. See [Authenticate: Signing a Request](/reference/signing-a-request-jwt-structure) for the full JWT structure. Install the SDK: ```bash theme={"system"} npm install @fireblocks/ts-sdk ``` Initialize: ```typescript TypeScript theme={"system"} import { Fireblocks, BasePath } from "@fireblocks/ts-sdk"; import fs from "fs"; const fireblocks = new Fireblocks({ apiKey: "", secretKey: fs.readFileSync("./fireblocks_secret.pem", "utf8"), basePath: BasePath.US, }); ``` SDK namespace for all trading operations: `fireblocks.tradingBeta.*` ### Required policy rules Configure the following policy rules in the Console before placing any orders. Set **Designated Signers** to specific named users; using **Initiator** on API-key-initiated orders will cause signing requests to stall unless a co-signer is configured. | Rule | Purpose | | - | - | | Order Policy | Approves the DVP settlement transaction that moves USDC from the source vault | | Approve (EVM only) | Grants Permit2 permission to move USDC. One-time per wallet + asset combination | | Typed Message (EVM only) | Signs the transfer authorization per order | | Program Call (Solana only) | Authorizes the Solana program call. No separate Approval needed | CPN settles through a smart contract, so it does not require a Transfer Policy rule; the Approve, Typed Message, and Program Call rules authorize the asset movement instead. ## Flow The integration follows this order: Discover requirements → Quote → Order → Sign → Track. If the order enters `AWAITING_INFORMATION` at any point, there is an additional stage for the Requirements (RFI) flow. ### Step 1: Discover CPN's PII requirements CPN requires participant identification (PII), and the required fields depend on the destination rail; each rail (PIX, SEPA, WIRE, SPEI, etc.) has its own PII requirements. There are two ways to discover them; select one, based on your integration preference: #### Up front, via the providers endpoint `GET /v1/trading/providers` Returns the available providers and, for CPN, its capabilities and participant-identification requirements per rail. Use this to build your data model before requesting a quote. See the [Get Providers reference](/api-reference/trading/get-providers). #### At quote time, via the quote response The quote response (Step 2) includes `requiredParticipantsIdentificationOnOrder`, a JSON Schema string describing the exact PII fields required for that specific corridor and amount. Parse it to build your order payload. Both options describe the same requirement. The first lets you prepare a PII collection before quoting; the second gives you the precise, corridor- and amount-specific schema at quote time. ### Step 2: Get a quote A quote returns a time-limited price for the conversion. CPN requires minimal PII at quote time; just entity types and country codes for originator and beneficiary. `POST /v1/trading/quotes` **Request Body** ```json theme={"system"} { "scope": [ { "providerId": "CPN", "accountId": "" } ], "baseAssetId": "USDC_SEP", "quoteAssetId": "BRL", "quoteAssetRail": "PIX", "baseAmount": "100.00", "side": "SELL", "participantsIdentification": { "originator": { "entityType": "INDIVIDUAL", "postalAddress": { "country": "US" } }, "beneficiary": { "entityType": "INDIVIDUAL", "postalAddress": { "country": "BR" } } } } ``` **Field notes:** * `scope`: ties the quote to a specific CPN account in your workspace. * `baseAssetId` / `quoteAssetId`: the sell and receive assets. For Solana use `SOL_USDC_JKVK`. * `quoteAssetRail`: `"PIX"`: required for BRL; tells CPN to route via the PIX rail. * `participantsIdentification`: CPN requires this at quote time. Minimum accepted values are entity type + country. **Expected Response (201)** ```json theme={"system"} { "quotes": [ { "id": "q_abc123...", "type": "COMMITTED", "via": { "providerId": "CPN", "accountId": "" }, "baseAssetId": "USDC_SEP", "quoteAssetId": "BRL", "baseAmount": "100.00", "quoteAmount": "520.00", "expiresAt": "2025-01-01T12:05:00.000Z", "requiredParticipantsIdentificationOnOrder": "{\"type\":\"object\",\"required\":[\"originator\",\"beneficiary\"],\"properties\":{...}}" } ], "quoteFailures": [] } ``` **Key field:** `requiredParticipantsIdentificationOnOrder`: a JSON Schema Draft-7 string describing the exact PII fields CPN requires on the order request. Parse it before building your order payload; the schema can vary by provider, corridor, and amount. Do not hardcode a PII structure; read it from the quote. **What to verify:** * `quotes[0].type` is `"COMMITTED"` (a firm, executable price). * `quoteAmount` is a non-zero BRL value. * `expiresAt` gives you a time window to place the order (typically 30–60 seconds). * `requiredParticipantsIdentificationOnOrder` is present and parseable as JSON Schema. ### Step 3: Create an order Use the `quoteId` from the previous step to create an order. The order must be placed before the quote's `expiresAt` timestamp. This step requires the full KYC PII payload validated against the JSON Schema returned in `requiredParticipantsIdentificationOnOrder`. `POST /v1/trading/orders` **Request Body** ```json theme={"system"} { "via": { "type": "PROVIDER_ACCOUNT", "providerId": "CPN", "accountId": "" }, "executionRequestDetails": { "type": "QUOTE", "quoteId": "" }, "settlement": { "type": "DVP", "sourceAccount": { "type": "VAULT_ACCOUNT", "accountId": "" }, "destinationAccount": { "type": "UNMANAGED_WALLET", "accountId": "" } }, "participantsIdentification": { "originator": { "entityType": "INDIVIDUAL", "fullName": { "firstName": "Jane", "lastName": "Doe" }, "postalAddress": { "streetName": "Main Street", "buildingNumber": "123", "postalCode": "10001", "city": "New York", "country": "US" }, "nationality": "US", "phone": "+15550123456", "dateOfBirth": "1990-01-01", "externalReferenceId": "internal-user-id-001" }, "beneficiary": { "entityType": "INDIVIDUAL", "fullName": { "firstName": "John", "lastName": "Silva" }, "postalAddress": { "streetName": "Rua das Flores", "buildingNumber": "1", "postalCode": "28990-001", "city": "Rio de Janeiro", "country": "BR" }, "nationality": "BR", "dateOfBirth": "1990-01-01", "externalReferenceId": "internal-beneficiary-id-001", "identificationDocuments": [ { "id": "390.533.447-05", "type": "NATIONAL_ID", "expirationDate": "2030-12-31" } ] } }, "sourceOfFunds": { "reasonForPayment": "PROPERTY_RENTAL" }, "customerInternalReferenceId": "test-order-ref-001" } ``` **Field notes:** * `via`: must match the provider and account used in the quote's scope. * `executionRequestDetails.type`: `"QUOTE"`: ties the order to a pre-fetched quote. The amount is already baked into the quote; do not specify it again. * `settlement.sourceAccount`: the vault holding the USDC to sell. * `settlement.destinationAccount`: the whitelisted external wallet that receives the BRL payout. * `participantsIdentification`: full KYC. The required fields are defined by the `requiredParticipantsIdentificationOnOrder` schema in the quote response. The example above covers the fields CPN requires for this corridor. * `sourceOfFunds.reasonForPayment`: required by CPN for cross-border compliance. Supported values: * `INVOICE_PAYMENT` * `SERVICES_PAYMENT` * `SOFTWARE_PAYMENT` * `IMPORTED_GOODS_PAYMENT` * `TRAVEL_SERVICES` * `TRANSFER_TO_OWN_ACCOUNT` * `LOAN_REPAYMENT` * `PAYROLL` * `PROPERTY_RENTAL` * `INFORMATION_SERVICE_CHARGES` * `ADVERTISING_AND_PUBLIC_RELATIONS` * `INTELLECTUAL_PROPERTY_FEES` * `FINANCIAL_SERVICE_FEES` * `ADVISORY_AND_TECHNICAL_FEES` * `REPRESENTATIVE_OFFICE_EXPENSES` * `TAX_PAYMENT` * `GOODS_TRANSPORTATION_FEES` * `CONSTRUCTION_EXPENSES` * `INSURANCE_PREMIUM` * `GENERAL_GOODS_TRADE` * `INSURANCE_CLAIMS_PAYMENT` * `FAMILY_REMITTANCE` * `EDUCATION_EXPENSES` * `MEDICAL_TREATMENT` * `DONATIONS` * `MUTUAL_FUND_INVESTMENT` * `CURRENCY_EXCHANGE` * `ADVANCE_GOODS_PAYMENT` * `MERCHANT_SETTLEMENT` * `REPATRIATION_FUND_SETTLEMENT` * `customerInternalReferenceId`: optional, but useful for correlating orders with your internal records. **Expected Response (202)** ```json theme={"system"} { "id": "ord_xyz789...", "status": "CREATED", "via": { "providerId": "CPN", "accountId": "" }, "createdAt": "2025-01-01T12:00:00.000Z", "updatedAt": "2025-01-01T12:00:00.000Z", "executionSteps": [] } ``` **What to verify:** * Response status is `202 Accepted`. * `order.id` (UUID) is returned; save this for the tracking step. * `order.status` is `"CREATED"`. ### Step 4: Smart contract settlement signing Once the order is created, the designated signer receives signing requests in the Fireblocks mobile app. The number and type of requests depend on the source network: **Ethereum / Polygon (EVM):** * **Permit2 approval** (first use only, per wallet + asset combination): a typed message signing request that grants CPN permission to move USDC from the source wallet via the Permit2 contract. This is a one-time operation; subsequent orders on the same wallet and asset will skip this step. * **Typed message signing**: the actual transfer authorization. The signer approves a structured EIP-712 message in the mobile app for each order. **Solana:** * **Program call signing**: the signer approves a Solana program instruction in the mobile app. There is no separate approval step; a single program call covers the full transfer. The order remains in `PROCESSING` status while signing requests are pending. If the designated signer does not approve within the policy timeout window, the order transitions to `FAILED`. Ensure the signer has the Fireblocks mobile app installed and push notifications enabled. ### Step 5: Track the order Poll the order status until it reaches a terminal state (`COMPLETED`, `FAILED`, or `CANCELED`), or until it enters `AWAITING_INFORMATION` which requires the RFI flow (see Step 6 below). `GET /v1/trading/orders/{orderId}` **Example** `GET /v1/trading/orders/ord_xyz789...` **Expected Response (200):** ```json theme={"system"} { "id": "ord_xyz789...", "status": "PROCESSING", "via": { "providerId": "CPN", "accountId": "" }, "createdAt": "2025-01-01T12:00:00.000Z", "updatedAt": "2025-01-01T12:00:30.000Z", "executionSteps": [ { "type": "BLOCKCHAIN_TRANSFER", "status": "PROCESSING", "txId": "tx_abc..." } ] } ``` **Order status lifecycle for a normal (happy path) flow:** ```text theme={"system"} CREATED → PROCESSING → COMPLETED ``` **Order status lifecycle when an RFI is triggered:** ```text theme={"system"} CREATED → PROCESSING → AWAITING_INFORMATION → PROCESSING → COMPLETED ↑ (submit RFI response) ``` **Polling recommendations:** * Poll every 5 seconds * Stop automatically when status is `COMPLETED`, `FAILED`, or `CANCELED` * Pause polling and enter the RFI flow when status is `AWAITING_INFORMATION` * On a `FAILED` order, check `failure.reason` and `failure.failureInfo` in the response for the provider error detail ### Step 6: Handle AWAITING\_INFORMATION (RFI flow) CPN may require additional information after an order is created, e.g. enhanced KYC documentation or clarification on source of funds. When this happens, the order status transitions to `AWAITING_INFORMATION`. You must submit the required information before the order can proceed. The RFI flow has three sub-steps: 1. Fetch the requirement to find out what is needed. 2. Upload any required files (one call per file). 3. Submit the structured data payload. #### 6.1 — Fetch the order requirement `GET /v1/trading/orders/{orderId}/requirement` **Example** `GET /v1/trading/orders/ord_xyz789.../requirement` **Expected Response (200)** ```json theme={"system"} { "requirementId": "req_abc123...", "dueBy": "2025-01-02T12:00:00.000Z", "requiredData": "{\"type\":\"object\",\"required\":[\"sourceOfFunds\",\"verificationMethod\"],\"properties\":{\"sourceOfFunds\":{\"type\":\"string\",\"enum\":[\"SALARY\",\"SAVINGS\",\"BUSINESS_INCOME\"]},\"verificationMethod\":{\"type\":\"string\",\"enum\":[\"GOVERNMENT_ID\",\"BANK_STATEMENT\"]}}}", "requiredFiles": [ { "fileKey": "identity_document", "description": "A government-issued photo ID (passport or national ID card).", "allowedFileTypes": ["PDF", "PNG", "JPEG"] } ] } ``` **Field notes:** * `requirementId`: unique identifier for this RFI instance. * `dueBy`: deadline to submit. If overdue, the order is likely to `FAIL` or be `CANCELED` shortly after. * `requiredData`: a JSON Schema Draft-7 string. Parse it to determine which fields and values to send in the data submission (step 6.3). * `requiredFiles`: list of files to upload before submitting data. Each entry specifies a `fileKey`, a human-readable description, and allowed MIME types. **Errors** * `409 Conflict`: the order exists but is not in `AWAITING_INFORMATION`. Check the current order status before calling this endpoint * `404 Not Found`: the order ID is invalid #### 6.2 — Upload required files For each entry in `requiredFiles`, upload the file using a `multipart/form-data` request. Call this endpoint once per file. `POST /v1/trading/orders/{orderId}/requirement/file` `Content-Type: multipart/form-data` **Form fields:** | Field | Value | | - | - | | `fileKey` | Matches the `fileKey` from the requirement (e.g., `identity_document`) | | `file` | The binary file content | **Example (curl)** ```bash theme={"system"} BOUNDARY="----FormBoundary$(date +%s)" curl -X POST "https:///v1/trading/orders/ord_xyz789.../requirement/file" \ -H "Authorization: Bearer " \ -H "X-API-Key: " \ -H "Content-Type: multipart/form-data; boundary=${BOUNDARY}" \ --data-binary $"--${BOUNDARY}\r\nContent-Disposition: form-data; name=\"fileKey\"\r\n\r\nidentity_document\r\n--${BOUNDARY}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"passport.pdf\"\r\nContent-Type: application/pdf\r\n\r\n$(cat /path/to/passport.pdf)\r\n--${BOUNDARY}--" ``` Alternatively, using `-F` (curl handles the boundary automatically): ```bash theme={"system"} curl -X POST "https:///v1/trading/orders/ord_xyz789.../requirement/file" \ -H "Authorization: Bearer " \ -H "X-API-Key: " \ -F "fileKey=identity_document" \ -F "file=@/path/to/passport.pdf;type=application/pdf" ``` **Expected Response:** `202 Accepted` (empty body) Fireblocks encrypts the file and forwards it to CPN. The file is not stored in plaintext. Upload all required files before submitting the data payload. #### 6.3 — Submit the data payload After uploading all files, submit the structured data that satisfies the `requiredData` JSON Schema. `POST /v1/trading/orders/{orderId}/requirement/data` **Request Body** ```json theme={"system"} { "data": { "sourceOfFunds": "SALARY", "verificationMethod": "GOVERNMENT_ID" } } ``` The exact fields depend on what `requiredData` schema was returned in step 6.1. Parse the schema dynamically; do not hardcode field names. **Expected Response:** `202 Accepted` (empty body) **What happens next:** CPN processes the submission asynchronously. After a successful submission, resume polling `GET /v1/trading/orders/{orderId}` until the order leaves `AWAITING_INFORMATION`. Expect a grace period of \~15 seconds before CPN updates the status. > [!NOTE] > > An order can enter `AWAITING_INFORMATION` more than once if CPN requires multiple rounds of verification. Each time, repeat steps 6.1–6.3. ## Order history List all orders for your workspace with optional filters. `GET /v1/trading/orders` Supported query parameters: `providerId`, `status`, `createdAfter`, `createdBefore`, `limit`, `next` (cursor for pagination). ## API reference summary All endpoints are prefixed with `/v1`. Production base URLs: | Region | Base URL | | - | - | | US | `https://api.fireblocks.io/v1` | | EU | `https://eu-api.fireblocks.io/v1` | | EU2 | `https://eu2-api.fireblocks.io/v1` | | Method | Path | Purpose | | - | - | - | | `GET` | `/trading/providers` | List providers and connected accounts; discover CPN's per-rail PII requirements | | `POST` | `/trading/quotes` | Create a quote | | `POST` | `/trading/orders` | Create an order from a quote | | `GET` | `/trading/orders/{orderId}` | Get current order status and details | | `GET` | `/trading/orders` | List orders (pagination + filters) | | `GET` | `/trading/orders/{orderId}/requirement` | Fetch RFI requirement details | | `POST` | `/trading/orders/{orderId}/requirement/file` | Upload a file for an RFI | | `POST` | `/trading/orders/{orderId}/requirement/data` | Submit structured data for an RFI | **Authentication:** Every request requires: * `Authorization: Bearer `: RS256 JWT signed with your API private key. * `X-API-Key: `: your workspace API key ID. **Permissions required:** * Quote + order creation: Owner, Admin, Non-Signing Admin, Signer, Editor * Order reads: all roles including Approver and Viewer * RFI submission: Owner, Admin, Non-Signing Admin, Signer, Editor # CLI Authentication Source: https://developers.fireblocks.com/docs/cli-authentication Configure credentials for the Fireblocks CLI. The CLI authenticates to the Fireblocks API using the same JWT-signing mechanism as the official SDKs: each request is signed with your RSA private key and identified by your API key. ## Prerequisites You need: * A **Fireblocks API key** — obtained from the Fireblocks Console under **Developer Center > API Users** * An **RSA private key** — the `.key` file generated when creating the API user See [Quickstart](/docs/quickstart) for instructions on creating an API user and generating a key pair. ## Credential Resolution Order The CLI resolves credentials in priority order: 1. **CLI flags** — `--api-key` and `--secret-key` passed directly on the command line 2. **Environment variables** — `FIREBLOCKS_API_KEY` and `FIREBLOCKS_SECRET_KEY` (or `FIREBLOCKS_SECRET_KEY_PATH`) 3. **Config file profile** — stored in `~/.config/fireblocks/config.json` If no credentials are found, the CLI exits with an error and instructions for how to configure them. ## Interactive Setup The easiest way to configure credentials is the interactive `configure` command: ```bash theme={"system"} fireblocks configure ``` This prompts for your API key, the path to your private key file, and optionally a base URL override. Credentials are written to `~/.config/fireblocks/config.json` with file permissions `0600`. To configure a named profile: ```bash theme={"system"} fireblocks configure --profile staging ``` To switch the default profile: ```bash theme={"system"} fireblocks configure --set-default=staging ``` ### Config File Format ```json theme={"system"} { "defaultProfile": "default", "profiles": { "default": { "apiKey": "your-api-key", "privateKeyPath": "/path/to/fireblocks_secret.key" }, "staging": { "apiKey": "staging-api-key", "privateKeyPath": "/path/to/staging_secret.key", "baseUrl": "https://sandbox-api.fireblocks.io" } } } ``` ## Environment Variables For CI/CD pipelines and non-interactive environments, set credentials as environment variables: | Variable | Description | | - | - | | `FIREBLOCKS_API_KEY` | Your Fireblocks API key | | `FIREBLOCKS_SECRET_KEY` | RSA private key — inline PEM content or a file path | | `FIREBLOCKS_SECRET_KEY_PATH` | File path to your RSA private key (alternative to `FIREBLOCKS_SECRET_KEY`) | | `FIREBLOCKS_BASE_URL` | Base URL override (default: `https://api.fireblocks.io`) | ```bash theme={"system"} export FIREBLOCKS_API_KEY="your-api-key" export FIREBLOCKS_SECRET_KEY_PATH="/path/to/fireblocks_secret.key" fireblocks vaults get-paged-vault-accounts ``` You can also pass inline PEM content directly — useful when pulling key material from a secrets manager: ```bash theme={"system"} export FIREBLOCKS_SECRET_KEY="-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAK... -----END RSA PRIVATE KEY-----" ``` ## CLI Flags Pass credentials directly on the command line to override environment variables and the config file: ```bash theme={"system"} fireblocks vaults get-paged-vault-accounts \ --api-key "your-api-key" \ --secret-key "/path/to/fireblocks_secret.key" ``` `--secret-key` accepts either a file path or an inline PEM string. ## Multiple Profiles Use profiles to manage multiple workspaces or environments: ```bash theme={"system"} # Configure a sandbox profile fireblocks configure --profile sandbox # Use the sandbox profile for a command fireblocks vaults get-paged-vault-accounts --profile sandbox ``` ## API Base URL The default base URL is `https://api.fireblocks.io`. Override it per command, in a profile, or via `FIREBLOCKS_BASE_URL`. | Environment | Base URL | | - | - | | US Mainnet / Testnet | `https://api.fireblocks.io` (default) | | US Sandbox | `https://sandbox-api.fireblocks.io` | | EU Mainnet / Testnet | `https://eu-api.fireblocks.io` | | EU2 Mainnet / Testnet | `https://eu2-api.fireblocks.io` | ```bash theme={"system"} fireblocks vaults get-paged-vault-accounts \ --base-url https://sandbox-api.fireblocks.io ``` ## Verify Your Setup Run `whoami` to confirm credentials are configured correctly and that the API key can authenticate: ```bash theme={"system"} fireblocks whoami ``` This calls `GET /v1/users/me` and returns the masked API key, base URL, and user details on success. To preview the resolved auth context without making a network call: ```bash theme={"system"} fireblocks whoami --dry-run ``` # CLI Usage Guide Source: https://developers.fireblocks.com/docs/cli-usage Commands, flags, examples, and error handling for the Fireblocks CLI. ## Command Pattern Every Fireblocks API operation is available as a command organized under a namespace: ```bash theme={"system"} fireblocks [flags] ``` Namespaces correspond to Fireblocks API resource groups (e.g. `vaults`, `transactions`, `assets`, `staking`). Examples: ```bash theme={"system"} fireblocks vaults get-paged-vault-accounts fireblocks transactions create-transaction --data '...' --no-confirm fireblocks staking get-chains ``` ## Discover Commands ### Full Command Index ```bash theme={"system"} fireblocks help-index ``` ### List Actions in a Namespace ```bash theme={"system"} fireblocks vaults --help ``` ### Show All Flags for a Command ```bash theme={"system"} fireblocks vaults get-vault-account --help ``` ### Preview a Request Use `--dry-run` on any command to see the exact request that would be sent — without executing it: ```bash theme={"system"} fireblocks transactions create-transaction \ --data '{"assetId":"BTC","amount":"0.001"}' \ --dry-run ``` Output: ```json theme={"system"} { "method": "POST", "url": "https://api.fireblocks.io/v1/transactions", "body": { "assetId": "BTC", "amount": "0.001" } } ``` ## Flags ### Path Parameters OpenAPI path parameters (e.g. `{vaultAccountId}`) become required flags with kebab-case names: ```bash theme={"system"} # GET /v1/vault/accounts/{vaultAccountId} fireblocks vaults get-vault-account --vault-account-id 0 ``` ### Query Parameters Query parameters become optional flags with the same kebab-case conversion: ```bash theme={"system"} # GET /v1/vault/accounts/paged?limit=50&namePrefix=hot fireblocks vaults get-paged-vault-accounts --limit 50 --name-prefix hot ``` ### Request Body Write operations (POST/PUT/PATCH/DELETE) accept a JSON request body via `--data`: ```bash theme={"system"} fireblocks transactions create-transaction \ --data '{"assetId":"ETH","amount":"0.1","source":{"type":"VAULT_ACCOUNT","id":"0"},"destination":{"type":"VAULT_ACCOUNT","id":"1"}}' \ --no-confirm ``` ### Global Flags These flags are available on every command: | Flag | Env Var | Description | | - | - | - | | `--api-key` | `FIREBLOCKS_API_KEY` | Fireblocks API key | | `--secret-key` | `FIREBLOCKS_SECRET_KEY` / `FIREBLOCKS_SECRET_KEY_PATH` | RSA private key — PEM content or file path | | `--base-url` | `FIREBLOCKS_BASE_URL` | Override the API base URL | | `--profile` | | Use a named credential profile | | `--dry-run` | | Preview the request without executing it | | `--no-confirm` | | Skip write-operation confirmation prompts | | `--debug` | | Log request and response details to stderr | | `-o, --output` | | Output format: `json` (default) or `yaml` | ## Write Operations POST, PUT, PATCH, and DELETE commands prompt for confirmation before executing: ```text theme={"system"} About to execute POST /v1/transactions. Continue? (y/N) ``` Skip the prompt for scripting and automation — either pass `--no-confirm` or pipe stdin from a non-TTY source: ```bash theme={"system"} fireblocks transactions create-transaction --data '...' --no-confirm ``` ### Idempotency Keys Endpoints that support idempotency accept `--idempotency-key`. Use it for safe retries on write operations so that each request runs at most once: ```bash theme={"system"} fireblocks vaults create-vault-account \ --data '{"name":"Treasury"}' \ --idempotency-key "$(uuidgen)" \ --no-confirm ``` > [!WARNING] > > Do not use `--idempotency-key` with `transactions create-transaction`. An idempotency key expires after 24 hours, so a transaction retried after that window would be created twice. Set a unique `externalTxId` in the `--data` body instead, because Fireblocks keeps it permanently and rejects any later transaction with the same value. See [Transaction idempotency](/reference/api-idempotency#transaction-idempotency). ```bash theme={"system"} fireblocks transactions create-transaction \ --data '{"assetId":"ETH","amount":"1.0","externalTxId":"","source":{"type":"VAULT_ACCOUNT","id":"0"},"destination":{"type":"ONE_TIME_ADDRESS","oneTimeAddress":{"address":"0xABC"}}}' \ --no-confirm ``` ## Output * **stdout** — JSON response data only * **stderr** — warnings, beta notices, errors, and debug logs ### Debug Logging Pass `--debug` to log request and response details to stderr: ```bash theme={"system"} fireblocks vaults get-vault-account --vault-account-id 0 --debug ``` ```text theme={"system"} [DEBUG] GET https://api.fireblocks.io/v1/vault/accounts/0 [DEBUG] Response: 200 [DEBUG] Request-ID: a1b2c3d4-... ``` ### Beta Commands Some commands are marked as beta. They print a warning to stderr before executing: ```text theme={"system"} Warning: This command is in beta and may change in future releases. ``` This is informational — the command still runs and produces output on stdout. ## Exit Codes | Code | Meaning | Recovery | | - | - | - | | `0` | Success | | | `1` | Client error (400 / 409 / 422) | Check request body or parameters | | `2` | Usage error (missing or invalid flag) | Check flag names and types | | `3` | Auth error (401 / 403) | Verify credentials with `fireblocks whoami` | | `4` | Not found (404) | Verify the resource ID exists | | `5` | Rate limited (429) | Wait `retry_after` seconds from the error JSON, then retry | | `6` | Server error (5xx) | Retry after a brief delay | | `7` | Timeout (30s) | Retry; the timeout is not configurable | ## Error Format Errors are written as JSON to **stderr**: ```json theme={"system"} { "code": 5, "status": 429, "message": "Rate limit exceeded", "request_id": "a1b2c3d4-5678-...", "retry_after": 30 } ``` `request_id` and `retry_after` are included only when applicable. ## Examples ```bash theme={"system"} # List vault accounts fireblocks vaults get-paged-vault-accounts # Get a specific vault fireblocks vaults get-vault-account --vault-account-id 0 # Create a transaction fireblocks transactions create-transaction \ --data '{"assetId":"BTC","amount":"0.001","source":{"type":"VAULT_ACCOUNT","id":"0"},"destination":{"type":"VAULT_ACCOUNT","id":"1"}}' \ --no-confirm # Idempotent transaction with retry safety (externalTxId, never --idempotency-key) fireblocks transactions create-transaction \ --data '{"assetId":"ETH","amount":"1.0","externalTxId":"","source":{"type":"VAULT_ACCOUNT","id":"0"},"destination":{"type":"ONE_TIME_ADDRESS","oneTimeAddress":{"address":"0xABC"}}}' \ --no-confirm # Preview a request without executing fireblocks transactions create-transaction \ --data '{"assetId":"BTC","amount":"0.001"}' \ --dry-run # Use the sandbox environment fireblocks vaults get-paged-vault-accounts \ --base-url https://sandbox-api.fireblocks.io # Debug a failing request fireblocks vaults get-vault-account --vault-account-id 0 --debug # Pipe output to jq fireblocks vaults get-paged-vault-accounts | jq '.accounts[].name' ``` ## Shell Autocomplete The CLI supports tab completion for Bash and Zsh. Run the autocomplete command for your shell and follow the printed setup instructions: ```bash theme={"system"} # Bash fireblocks autocomplete bash # Zsh fireblocks autocomplete zsh ``` # Compliance Events & Webhooks Source: https://developers.fireblocks.com/docs/compliance-events-webhooks Subscribe to compliance webhook events, read screening results from them, and handle them reliably. Use these events to respond to screening results rather than polling. ## Events | What you want to know | Event | Subscription | | - | - | - | | A transaction started screening, or was accepted, rejected, or frozen, for any check | `transaction.status.updated` (Webhooks v1: `TRANSACTION_STATUS_UPDATED`) | Webhooks v2 | | A Sumsub or GTR Travel Rule message needs more data, or is missing | `TRLink.RequiredActions`, `TRLink.NoTRMessage` | **Compliance** notification category | ![Outgoing and incoming transaction status changes, each sending a transaction.status.updated webhook event](https://mintcdn.com/fireblocks-43c4b3ee/UNwBNbbfA7SOB2NL/images/docs/ic-compliance-status-events.jpg?fit=max\&auto=format\&n=UNwBNbbfA7SOB2NL\&q=85\&s=3af9c16f7a9ad90b2824b053a7d0a2ba) Fireblocks sends `transaction.status.updated` at every status change, outgoing and incoming. For what each `status` and `subStatus` means, see [Transaction states](/docs/transaction-screening-operations#transaction-states). ## Subscribe * **Webhooks v2:** set up an endpoint and subscribe to the events you need. See [Webhooks Overview](/reference/webhooks-overview). * **Compliance notifications:** in the Console, go to **Settings** > **Notifications** > **Webhooks** > **Create Webhook**, and select **Compliance**. Then handle events reliably: * **Verify** each event's signature before processing it. See [Validating webhooks](/reference/validating-webhooks). * **Acknowledge fast:** return `200` right away and process asynchronously. * **Do not rely on order:** events can arrive out of order. Compare against the transaction's current state. * **Deduplicate** on the notification `id`. For Travel Rule notifications, use `destinationScreeningId` when present; otherwise, use the envelope `id`. * **Re-fetch before acting:** read the current state from the API before bypassing, rescreening, unfreezing, or submitting a verdict. * **Recover missed events:** Webhooks v2 can resend notifications for up to 30 days. See [Resending & troubleshooting webhook notifications](/reference/webhooks-resend-troubleshooting). ## Get full details on a status event The `data` object of `transaction.status.updated` is the full transaction. Its `subStatus` does not say which check made the decision; read the `complianceResults` object, which carries every check's result (provider, verdict, risk level) when the event includes it. `amlScreeningResult` is a legacy object; use `complianceResults` instead. For the raw provider payload, call [`GET /screening/transaction/{txId}`](/api-reference/compliance/provides-all-the-compliance-details-for-the-given-screened-transaction). See [Read the result](/docs/transaction-screening-operations#read-the-result) for the fields. ```ts theme={"system"} app.post("/webhooks/fireblocks", async (req, res) => { res.sendStatus(200); // acknowledge first, then process const event = req.body; if (event.eventType !== "transaction.status.updated") return; const tx = event.data; if (tx.status === "REJECTED" && tx.subStatus === "REJECTED_AML_SCREENING") { const { data } = await fireblocks.compliance.getScreeningFullDetails({ txId: tx.id }); await notifyCompliance(tx.id, data.aml?.verdict, data.aml?.risk); } }); ``` ## Travel Rule notifications (Sumsub, GTR) These are audit-log notifications in the **Compliance** category (`categoryId: "7"`), not Webhooks v2 events. Tell them apart by `note.messageType`. `note` is a JSON-encoded string: parse it first. Only `screeningMessage` and `messageType` are always present. **`TRLink.RequiredActions`**: a Travel Rule message needs more data. Sent once per destination. | Field | Description | | - | - | | `trmId` | The Travel Rule message. | | `customerIntegrationId` | The integration to call the resolve endpoints with. | | `requiredActions` | The actions to take, each with a `type`, `description`, and `data`. Never empty. | | `destinationScreeningId` | The destination this applies to, when the notification comes from destination screening. For tracking and deduplication only; to link a message to a destination, match by `amount` and `destination`. | **`TRLink.NoTRMessage`**: a transaction that needs a Travel Rule message has none. Includes the `txId`, `destinationScreeningId`, and destination details. Create and link a message, or submit a manual decision. For both flows, see [Sumsub](/docs/sumsub) and [GTR](/docs/gtr). ## On the Help Center There is no Help Center equivalent. In the Console, screening results appear in **Transaction History** > **Transaction Details**. # Compliance Policies Source: https://developers.fireblocks.com/docs/compliance-policies Read compliance policy rules, configure outage and delay behavior, and set workspace-wide limits through the Fireblocks API. Each check type has its own compliance policy, built in the Console under **Policies** > **Compliance**. Outage and delay behavior and workspace-wide limits are settings outside the rules. To act on a single screened transaction, see [Transaction Screening Operations](/docs/transaction-screening-operations). ## How a policy works A policy has two parts, three for Sumsub and GTR, each a list of if-then rules. Fireblocks evaluates rules top to bottom; the first match decides the action, and the last rule is always a catch-all default. | Part | Decides | Actions | | - | - | - | | Trigger (Screening Policy) | Which transactions are screened | Screen, Pass, Freeze | | Missing Travel Rule Message (Sumsub and GTR only) | What happens while a transaction that needs review has no Travel Rule message yet | Wait, Accept, Reject | | Outcome (Post-Screening Policy) | What happens to the result | Accept, Reject, Alert (every provider); Wait (Notabene, Sumsub, GTR, TRM Labs); Freeze, Cancel (Notabene) | Fireblocks sends a transaction to the provider once per screening, per destination, regardless of how many rules match. **For an outgoing transaction with several destinations**, each destination is screened, and the most restrictive result determines the outcome: freeze takes precedence, then reject, alert, accept. All results are returned in `complianceResults.amlList`; the deciding one is `complianceResults.aml`. Sumsub and GTR apply the same rule (reject, wait, alert, accept). Notabene works per transaction. Bring Your Own Screening gives one decision per transaction. TRM Labs returns only the worst result in `amlList`. Incoming AML/KYT screening supports one destination; transfers with more are bypassed as `BYPASSED_FAILURE`. A result is a risk level for AML/KYT, a message status for Travel Rule, or a group match for Address Registry Screening. Example: a Trigger rule "if an outbound ETH transaction is over 10,000 USD, screen it", and an Outcome rule "if the risk level is high, reject it". To build rules, see [Building a Compliance Policy](https://support.fireblocks.io/hc/en-us/articles/30614325308828-Building-a-Compliance-Policy) on the Help Center. ## What you can do through the API | Task | Console | API | SDK method | | - | - | - | - | | Read the AML/KYT Trigger | Yes | [`GET /screening/aml/screening_policy`](/api-reference/compliance/aml--view-screening-policy) | `compliance.getAmlScreeningPolicy` | | Read the AML/KYT Outcome | Yes | [`GET /screening/aml/post_screening_policy`](/api-reference/compliance/aml--view-post-screening-policy) | `compliance.getAmlPostScreeningPolicy` | | Read the Notabene Trigger | Yes | [`GET /screening/travel_rule/screening_policy`](/api-reference/compliance/travel-rule--view-screening-policy) | `compliance.getScreeningPolicy` | | Read the Notabene Outcome | Yes | [`GET /screening/travel_rule/post_screening_policy`](/api-reference/compliance/travel-rule--view-post-screening-policy) | `compliance.getPostScreeningPolicy` | | Read the Sumsub and GTR policy (all three stages) | No | [Get TRLink policy](/api-reference/trlink/get-trlink-policy) | See the API Reference | | Read the AML/KYT outage and delay settings | Yes | [`GET /screening/aml/policy_configuration`](/api-reference/compliance-screening-configuration/get-aml-screening-policy-configuration) | `complianceScreeningConfiguration.getAmlScreeningConfiguration` | | Update the AML/KYT outage and delay settings | Yes | [`PUT /screening/aml/policy_configuration`](/api-reference/compliance/update-aml-configuration) | See the API Reference | | Read the Travel Rule outage and delay settings | Yes | [`GET /screening/travel_rule/policy_configuration`](/api-reference/compliance-screening-configuration/get-travel-rule-screening-policy-configuration) | See the API Reference | | Update the Travel Rule outage and delay settings | Yes | [`PUT /screening/travel_rule/policy_configuration`](/api-reference/compliance/update-travel-rule-configuration) | See the API Reference | | Set workspace-wide limits on bypass and unfreeze | Yes | [`PUT /screening/configurations`](/api-reference/compliance/tenant--screening-configuration) | See the API Reference | | Read the Address Registry Screening policy, set its timeout | Yes | No | | | Build, edit, and delete rules | Yes; Sumsub and GTR through Fireblocks Support | No | | Rules return in evaluation order. To change Sumsub or GTR rules, submit a policy template to Fireblocks Support, copying your workspace Owner, whose approval is required. ## Outage and delay settings Settings for when a provider is unreachable or slow. They apply per check type, not per provider, with one exception: `bypassScreeningDuringServiceOutages` does not apply to TRM Labs, whose timeout behavior comes from its own policy rules. | Field | Type | Description | | - | - | - | | `bypassScreeningDuringServiceOutages` | boolean | Default `true`: a missed result bypasses screening (outgoing is sent, incoming is released). `false`: a missed result fails the transaction (outgoing is not sent, incoming is frozen). | | `inboundTransactionDelay` | number | Seconds to wait for an inbound result before the bypass behavior applies. | | `outboundTransactionDelay` | number | Seconds to wait for an outbound result before the bypass behavior applies. | Defaults and maximums vary by check type and provider. Updates accept an optional `Idempotency-Key` header, valid for 24 hours. ```ts theme={"system"} const { data } = await fireblocks.complianceScreeningConfiguration.getAmlScreeningConfiguration(); console.log(data.bypassScreeningDuringServiceOutages, data.inboundTransactionDelay); ``` ## Workspace-wide limits A blocked operation requires a Fireblocks Support ticket instead. | Field | Type | Description | | - | - | - | | `disableBypass` | boolean | Blocks bypassing a rejected outgoing transaction. | | `disableUnfreeze` | boolean | Blocks unfreezing a frozen incoming transaction. | ## Limitations * **One policy per connected tool.** No single policy applies across every tool. * **Unscreened routes, AML/KYT and Notabene only:** vault-to-vault, vault-to-exchange, vault-to-Gas Station, Gas Station-to-vault, and non-custodial wallet to non-custodial wallet within the same workspace. ## On the Help Center To build rules in the Console, see [Building a Compliance Policy](https://support.fireblocks.io/hc/en-us/articles/30614325308828-Building-a-Compliance-Policy). # Configure Gas Station Values Source: https://developers.fireblocks.com/docs/configure-gas-station-values You can configure the Fireblocks Gas Station with a gas threshold, a gas cap, and a maximum gas price for each blockchain network according to its base asset by using the Fireblocks API. * **Gas threshold (gasThreshold):** The gas threshold represents the minimum base asset balance allowed in an auto-fueled vault account. If the balance is below the minimum gas threshold, the Gas Station fuels the vault account automatically from the Gas Station tank, up to the gas cap. * **Gas cap (gasCap):** The gas cap represents the maximum base asset balance the Gas Station fuels an auto-fueled vault account up to. * **Maximum gas price (maxGasPrice):** The maximum gas price allows you to limit the maximum transaction fee for Gas Station transactions. The funding transaction will be sent in gwei with this maximum value gas price or less. A null value for the maximum gas price means the fee will be paid at any cost, without limitations. If you changed the maximum gas price settings and want to revert to the default settings of null, you can enter an empty string as `""`. When an incoming token deposit or an outgoing token withdrawal is detected, the Gas Station checks the gas balance of the vault account. If the balance is below the minimum gas threshold, it automatically fuels the vault account from the Gas Station tank, up to the gas cap. For configuration details, see the Fireblocks Gas Station [developer guide](/reference/set-the-gas-station-values). # Connect to Exchanges and Fiat Providers Source: https://developers.fireblocks.com/docs/connect-to-exchanges-and-fiat-providers Fireblocks enables clients to connect their own (supported by Fireblocks) exchange and fiat accounts directly within the workspace. Customers can bring their API credentials from the supported exchange or fiat account and integrate them into the Fireblocks workspace. Once connected, clients can seamlessly transfer funds to and from the connected exchange account via the Fireblocks console or the API. Additionally, clients can view the balances of their connected exchange accounts and set Policies and automated processes around these accounts, enhancing both control and efficiency in managing their assets. > [!NOTE] > > **Learn more about connecting Exchange or Fiat accounts in the following guides:** > > 1. [Supported Exchange Accounts and configuration guides](https://support.fireblocks.io/hc/en-us/sections/17902666998300-Connecting-Provider-Accounts) > 2. [Supported FIAT Accounts and configuration guides](https://support.fireblocks.io/hc/en-us/articles/360018286760-Fiat-accounts) For API details, see the [Exchange Accounts](/reference/getexchangeaccounts) and [Fiat Accounts](/reference/getfiataccounts) management APIs in the Fireblocks API Reference and the [Exchange and Fiat Accounts Developer Guide](/reference/connect-to-exchanges-fiat-providers). # Connect to the Fireblocks Network Source: https://developers.fireblocks.com/docs/connect-to-the-fireblocks-network The Fireblocks Network is a peer-to-peer, institutional liquidity and transfer network of 1,500+ liquidity providers, lending desks, and trading counterparties. Members can discover, connect, and settle with other Fireblocks Network members quickly, easily, and securely using features available only to Network members. # Fireblocks Network features To use Fireblocks Network features with a counterparty, connect to each other on the Network. Whether you prefer to make your Network profile discoverable or not, you can connect with other members in several ways. Discoverable members show up in searches for connections. You can then request to add them as a connection from the search results. To connect with non-discoverable Network members you need to know their unique Network ID. If your profile is not discoverable, you can still search for other discoverable members and request to connect with them. Both your Admin Quorum as well as your counterparty’s Admin Quorum must approve new Network connections. When you make transfers on the Fireblocks Network, you benefit from these Network features: ## Automated Address Authentication (AAA) All Fireblocks Network transfers route through an encrypted tunnel within a secure hardware enclave. With AAA, you never have to store or share deposit addresses in poorly secured and error-prone ways, such as messenger apps or copying and pasting, which means you can: * **Transfer faster:** Avoid time-consuming processes like manually whitelisting new deposit addresses, sending test transfers, and finding, copying, and pasting deposit addresses to send or receive a digital asset transfer. * **Prevent errors:** Avoid manual, error-prone processes like tracking and copying and pasting addresses that can cause your assets or your counterparty’s assets to go to the wrong address. * **Secure transfers:** Prevent Man-in-the-Middle and Address Spoofing attacks, where hackers use malware to replace addresses on infected devices, and intercept and replace addresses while you share them over messenger apps or copy and paste them. With AAA, you don’t need to manually manage or share deposit addresses. * **Future-proof connections:** With AAA, connected counterparties stay connected even if your deposit address changes. AAA will automatically remap it without resharing or reconnecting. ## Flexible Network Routing When you add new Network connections, you can define how incoming deposits are routed. You can tailor routing for Network deposits of both crypto and fiat in two ways * **Route by Network profile:** Send all deposits from connections to your Network profile to a designated vault account, exchange account, or fiat account in your workspace. * **Route by connection:** Tailor routing per connection. Each connection’s deposits can go to their own specific designated vault account, exchange account, or fiat account. You can also execute all Flexible Network Routing configuration actions using the Fireblocks API. With maximum flexibility for routing and managing deposit addresses for Network transfers, you can capitalize on more opportunities faster and more easily using the Fireblocks Network. ## Automatic Address Rotation When you transfer Bitcoin or other UTXO-based tokens with Fireblocks Network connections, your deposit addresses can rotate automatically for every incoming transfer. For certain tokens, a different unique identifier is rotated on every Network transfer instead of rotating addresses. * For Ripple transfers on the Network, the deposit address stays the same, but the unique XRP tag is rotated every transfer. * For EOS, Hedera, and Stellar transfers on the Network, the deposit address stays the same, but the unique memo ID is rotated every transfer. For Network transfers on account-based blockchains like Ethereum, Automatic Address Rotation isn’t supported and there is no other unique identifier. You can transfer account-based assets on the Fireblocks Network. However, you can’t create unique identifiers like a deposit address, tag, or memo ID for each transfer for pseudo-anonymity on the blockchain. Since Network transfers also use Automated Address Authentication, you don’t have to track or share the new addresses. This enables you to: * Preserve privacy on the blockchain by preventing your transaction history from being easily discovered by someone searching for a deposit address. * Avoid errors from manual address rotation, like forgetting to update a connection with your new address or a copy-and-paste error while tracking or sharing. > [!NOTE] > > Learn how to connect over the Fireblocks Network in the Web Console [here](https://support.fireblocks.io/hc/en-us/articles/6107078528540-Configuring-your-Fireblocks-Network-profile). For API details, see the [Fireblocks Network APIs](/reference/getnetworkconnections) in the Fireblocks API reference. # Create Direct Custody Wallets Source: https://developers.fireblocks.com/docs/create-direct-custody-wallets The Fireblocks platform consists of workspaces, vault accounts, asset wallets, and deposit addresses. This diagram shows the overall account structure in Fireblocks and the underlying wallet structure. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/de5e145-mceclip0.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=33dc6b604651a6246a6d1f8eaa4b66f4) At the top level, your various Fireblocks workspaces are contained within the logical group called the Customer Domain. These workspaces may be "hot" (online) or "cold" (offline). In your Fireblocks workspace, you can create and manage multiple vault accounts. These vault accounts contain your asset wallets. Each vault account can have a single wallet per base asset (e.g., BTC, ETH, SOL), but each wallet can have one or more deposit addresses. For UTXO-based assets, such as Bitcoin, a single wallet may generate one or more deposit addresses. For account-based assets, there are two options: * Account-based assets without tag/memo support, such as ETH, can only generate one deposit address. * Account-based assets with tag/memo support, such as XRP, can generate one or more deposit addresses. Each deposit address has the same on-chain address. However, they differ by their tag/memo. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/1003eba-mceclip0_1.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=82f94e39d01a9d54fbef221ce07671eb) ## Automatically generated asset wallets ### EVM * On EVM chains, a single deposit address is shared across all EVM asset wallets in the vault account, regardless of chain or token. * Asset wallets are auto-created when an asset is transferred to a deposit address in a vault account that doesn't yet contain that asset wallet. Applies to all globally listed EVM assets. * For non-globally-listed assets: user must first add the asset via the Add Asset API endpoint, after which auto-creation applies. ### Solana * Each token requires its own on-chain token account in addition to the Fireblocks asset wallet. * The vault account uses the base Solana address as deposit address. * When a token wallet is created via API or Console, Fireblocks creates an internal representation only; there is no on-chain transaction at that point. This saves rent expenses and follows common Solana practices in which the sender of a token creates the address. * Token account is created on-chain on the first incoming transaction; sender covers the rent cost. * When the asset wallet is created due to an incoming transfer, the token account already exists on-chain. * Asset wallets are auto-created when an asset is transferred to a deposit address in a vault account that doesn't yet contain that asset wallet. Applies to all globally listed Solana assets. * For non-globally-listed assets: user must first add the asset via the Add Asset API endpoint, after which auto-creation applies. ### Hedera * Each Hedera Token Service (HTS) token must be associated with the account on-chain before the account can receive that token, in addition to the Fireblocks asset wallet. * The vault account uses the base HBAR address as the deposit address. * When a token wallet is created via API or Console, Fireblocks creates an internal representation only by default; there is no on-chain association transaction at that point. * When you create the token wallet, select one of two funding options: the first sender covers the association fee on the first incoming transfer, or the association is funded from the vault's HBAR balance immediately after wallet creation. See [Hedera HTS tokens](/reference/create-vault-wallet#hedera-hts-tokens) for API details. * Sender-funded association follows the same common practice as Solana token accounts, where the first sender funds the token account. However, not all senders support transfers to accounts that are not yet associated with the token. Fireblocks supports outgoing transfers to destinations that are not associated with the token. * HBAR wallets created before July 2026 do not support sender-funded association. For these wallets, associate each HTS token before receiving deposits by creating the token wallet with the **Fund from the vault HBAR balance** option. * Deposits to an account that is not associated with the token, where the sender does not cover the association fee, fail on-chain with the `TOKEN_NOT_ASSOCIATED_TO_ACCOUNT` error. # Types of vault structures Fireblocks recommends structuring your vault accounts using one of two methods: omnibus or segregated. The diagram below describes the advantages and disadvantages of each structure. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/da10aee-mceclip1.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=eb69720cc0735849fc33070492e19e84) ## Omnibus structure The omnibus structure consists of a central vault account, deposits vault account for UTXO and Tag/Memo based assets and vault accounts for each end-client for account based assets. Funds are deposited into the individual vault accounts or to the deposits vault account, depending on the asset type, and then swept to the central vault account, where the funds can be invested. ### Address creation flows For extended documentation, see [Best Practices for Structuring Your Fireblocks Vault](https://support.fireblocks.io/hc/en-us/articles/5253421857564-Fireblocks-Vault-structure-best-practices). Some notes on the information presented in the Help Center article: * Omnibus and segregated account structures are independent of whether you have a hot or cold workspace. Both account structures can be implemented in either workspace type. * Workspaces can contain both an omnibus and segregated account models. There is no limitation for a workspace to contain one specific account model. #### Omnibus address creation (UTXO) ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/7cf2aee-mceclip4.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=772fd3814466a15bca45891373f171ee) * The end user, Alice, asks to deposit a UTXO asset, such as Bitcoin (BTC). * The customer's application creates a new deposit address in the Omnibus vault account. * This deposit address is assigned to Alice and mapped accordingly within the customer’s private ledger. * Alice receives a notification from the application with her assigned deposit address, where she can start depositing funds. #### Omnibus address creation (account-based with tag/memo support) ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/e64b2d0-mceclip5.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=2274edd853e7cc70ce154da6380f556f) * The end user, Alice, asks to deposit an account-based asset that supports tag/memo fields, such as Ripple (XRP). * The customer's application creates a new deposit address in the Omnibus vault account. * This deposit address is assigned to Alice and mapped accordingly within the customer’s private ledger. * Alice receives a notification from the application with her assigned deposit address, where she can start depositing funds. #### Omnibus address Creation (account-based) ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/1ee8808-mceclip6.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=14f261f049ae47d03e9c1ba76be8e0f6) * The end user, Alice, asks to deposit an account-based asset, such as Ethereum (ETH). * The customer's application creates a new intermediate vault account and a wallet with a deposit address within it. * This deposit address is assigned to Alice and mapped accordingly within the customer’s private ledger. * Once mapped and credited, Alice receives a notification from the application with her assigned deposit address, where she can start depositing funds. ## Segregated structure The segregated structure consists of individual vault accounts for each end client. Funds are stored in and invested from these individual accounts. ### Address creation flows There’s no difference between UTXO and account-based assets when dealing with the segregated account structure. For both, the process is as follows: ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/8ce1c5b-mceclip10.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=a1a698af9f6b283011aad4bf7fb039dd) * The end user, Alice, asks to deposit some assets. * The customer's application creates or reuses a vault account dedicated to Alice, then creates at least one asset wallet to generate a deposit address (if one doesn't already exist). After that, Fireblocks automatically creates an asset wallet for any globally listed asset sent to that address. * Alice receives a notification from the application with her assigned deposit address, where she can start depositing funds. # Hiding vault accounts When you create intermediate vault accounts for end users, your workspace can quickly accumulate a large number of them. Sometimes tens of thousands, sometimes millions. Having all of these accounts visible in the Fireblocks Console creates two problems. First, it degrades console performance. Second, every transaction to or from these accounts appears in the Recent activity panel, which can flood the interface with notifications when deposits are frequent. Since intermediate vault accounts are managed through the API anyway, it makes sense to hide them from the Console. You have two ways to do this, both through the Fireblocks API: * **Hide a vault account at creation.** Pass the optional `hiddenOnUI` parameter when calling the [Create a new vault account](/reference/createvaultaccount) endpoint. * **Hide an existing vault account.** Use the [Hide a vault account in the Console](/reference/hidevaultaccount) endpoint for any account that wasn't hidden at creation. Hidden vault accounts are not gone. All transactions and balances remain fully accessible through the API. # Overview Source: https://developers.fireblocks.com/docs/create-payouts > [!NOTE] > > Fireblocks provides technology infrastructure only. All payment and financial services described on this page are delivered by our customers under their own regulatory licenses. Fireblocks does not provide regulated services in the EEA. The Payout Service API allows you to create and execute payout instruction sets. A payout instruction set describes how to distribute payments from a payment account to multiple payee accounts. * **Payment account:** Any valid Fireblocks-supported vault account, exchange account, or fiat account. * **Payee account:** A valid address in Fireblocks for receiving funds. This can be a vault account, exchange account, whitelisted address, fiat account, or merchant account. The payout process can only be executed if the payment account has a sufficient balance for the entire payout instruction set. If the payout account doesn't have a sufficient balance, the payout remains in an Insufficient Balance state until the payment account has a sufficient balance to perform the payout instructions. Payouts in an Insufficient Balance state expire at midnight UTC and are moved to Failed status. A payout instruction set can only be executed once. Re-executing a payout instruction set results in an error. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/d3f16e9-swimlanes-0824f23d87b6476b8fce5eb945f197cd.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=f6b4e4b0fe5147c64a62e2a6dbd9c645) # Payouts Flow When a user wants to send a payment to one or more of their merchants, they can use the Payout Service API to send the payout from their exchange account. The flow consists of 3 main steps: 1. [Creating a payout instruction set](/reference/executing-payouts#creating-a-payout-instruction-set) 2. [Executing a payout instruction set](/reference/executing-payouts#executing-a-payout-instruction-set) 3. [Monitoring Payouts](/reference/executing-payouts#executing-a-payout-instruction-set) # Customer Reference ID Source: https://developers.fireblocks.com/docs/customer-reference-id Tag vault accounts, wallets, and transactions with your own customer identifier through the Fireblocks API. `customerRefId` is an optional identifier that Fireblocks forwards to your AML/KYT provider, so you can match a transaction, wallet, or account to your own record in the provider's system. It is a reference field only: it is not a Trigger (Screening Policy) condition and does not affect any Outcome (Post-Screening Policy). ## Choose a value Use an anonymized token, such as a UUID, that you map to the customer in your own systems; Fireblocks does not store client PII. The value must match `^[-_:a-zA-Z0-9]*$`: letters, digits, hyphens, underscores, and colons. ## Which value applies Fireblocks uses the first value it finds, in this order: 1. The transaction's own destination value. 2. For incoming transactions, the deposit address's value. 3. The vault account's value. 4. An external or unmanaged wallet's value. 5. A workspace-wide default, if set. If none is set, Fireblocks sends the literal value `DEFAULT`. ## What you can do through the API | Level | When | Endpoint | | - | - | - | | Vault account | When you create it | `customerRefId` in [`POST /vault/accounts`](/api-reference/vaults/create-a-new-vault-account) | | Vault account | After it exists | [`POST /vault/accounts/{vaultAccountId}/set_customer_ref_id`](/api-reference/vaults/set-an-amlkyt-id-for-a-vault-account) | | Deposit address | When you create it, or after | [`POST /vault/accounts/{vaultAccountId}/{assetId}/addresses/{addressId}/set_customer_ref_id`](/api-reference/vaults/assign-aml-customer-reference-id) | | Internal wallet | After it exists | [Set customer reference ID for an internal wallet](/api-reference/internal-wallets/set-an-amlkyt-customer-reference-id-for-internal-wallet) | | External wallet | After it exists | [`POST /external_wallets/{walletId}/set_customer_ref_id`](/api-reference/external-wallets/set-an-aml-customer-reference-id-for-an-external-wallet) | | Transaction | When you create it, per destination | `customerRefId` on each destination in [`POST /transactions`](/api-reference/transactions/create-a-new-transaction) | Setting it on a vault account or deposit address requires the Admin or Non-Signing Admin role. ## Examples On a vault account: ```json theme={"system"} POST /v1/vault/accounts { "name": "MyVaultAccount", "customerRefId": "customer-1234" } ``` Per destination on a transaction: ```json theme={"system"} POST /v1/transactions { "destinations": [ { "amount": "0.5", "customerRefId": "customer-1234" } ] } ``` ## Limitations * API only; there is no Console field. * Sent only to your own AML/KYT provider, never to the counterparty or receiving institution. ## On the Help Center There is no Help Center equivalent; Customer Reference ID is available through the API only. # Data Privacy and Protection Source: https://developers.fireblocks.com/docs/data-privacy-and-protection Fireblocks does not store or process any client Personal Identifiable Information (PII). To achieve this, the customer is required to strictly use anonymized tokens, such as (UUID) to reflect client data. These anonymous tokens are mapped internally by the customer to actual client identifiers stored on the customer's end. Since the customer processes and manages its wallet infrastructure using the Fireblocks Console or Fireblocks API, these anonymous tokens should be used when using the Fireblocks REST API endpoints. The following table lists the relevant fields that allow persistent information to be stored in Fireblocks–for which the anonymous tokens should be used. | API Endpoint | Method | Parameter | | - | - | - | | Vault | [Create a new vault account](/api-reference/vaults/create-a-new-vault-account) | name | | Vault | [Create a new asset deposit address](/reference/createvaultaccountassetaddress) | description | | Vault | [Rename a vault account](/api-reference/vaults/rename-a-vault-account) | description | | Internal Wallet | [Create an internal wallet](/api-reference/internal-wallets/create-an-internal-wallet) | name | | Internal Wallet | [Set an AML/KYT customer reference ID for an internal wallet](/api-reference/internal-wallets/set-an-amlkyt-customer-reference-id-for-internal-wallet) | customerRefId | | External Wallet | [Create an external wallet](/api-reference/external-wallets/create-an-external-wallet) | name | | External Wallet | [Set an AML customer reference ID for an external wallet](/api-reference/external-wallets/set-an-aml-customer-reference-id-for-an-external-wallet) | customerRefId | | Contracts | [Create a contract](/api-reference/contracts/add-a-contract) | name | | Transactions | [Create a new transaction](/api-reference/transactions/create-a-new-transaction) | note | | Transactions | [Estimate a transaction fee](/api-reference/transactions/estimate-transaction-fee) | note | # Define Approval Quorums Source: https://developers.fireblocks.com/docs/define-approval-quorums # Admin Quorum The Admin Quorum lists all users with Admin privileges (users assigned to either an Owner, Admin, or Non-Signing Admin role). The Admin Quorum threshold defines the number of Admins required to approve new workspace connections and changes. Any Admin can deny a request to reject it before the threshold is met. Requests have different expiration times depending on the action. Activities that require Admin Quorum approval include: * Whitelisting addresses * New Fireblocks Network connections * New exchange accounts * Other external destination addresses * Adding new workspace users * Changes to Policies * Configuring approval groups * Other workspace configuration changes For more details, see the [Admin Quorum guide](https://support.fireblocks.io/hc/en-us/articles/29110816772380-Admin-quorum). ## Admin Quorum API Check the Admin Quorum API reference [here](/reference/setadminquorumthreshold) # Approval Groups Approval groups consist of users from a designated user group. They are designed to facilitate the approval of various workspace configuration changes within different workspace domains, such as security and compliance, user management, the Fireblocks Network, and external accounts.\ These groups operate similarly to the Admin Quorum and can be used in its place for approving specific actions of your choice. For more details, see the [Approval Groups guide](https://support.fireblocks.io/hc/en-us/articles/29184265622556-Approval-groups). This feature allows you to designate a specific group of people to approve tasks, regardless of their roles or permissions. Approval groups provide the flexibility to segregate and delegate responsibilities, which is crucial for scaling and accelerating your business operations while maintaining security. Protected tags use two approval groups: one for editing and deleting a protected tag, and one for attaching and detaching it. Configure them in **Settings** > **Quorums** > **Approval Groups**. See [Tags](/docs/tags#protected-tags-and-the-approval-flow). Here are a few tips for setting up your approval groups: * Approval groups are distinct from regular user groups and are not related to Policies * While you can remove owner approval from certain actions, key changes in your workspace will still require owner involvement * A threshold for the chosen group is required # Overview Source: https://developers.fireblocks.com/docs/define-confirmation-policy Blockchain confirmations are crucial for maintaining the security and integrity of the blockchain. This process ensures that transactions are securely verified, thereby preventing fraud and double-spending. However, the number of confirmations also directly impacts the time it takes for a transaction to complete and for the funds to become available in your wallet. The higher the number of required confirmations, the longer it takes for the transferred amount to be accessible. Whether you aim to speed up transaction flow by reducing the number of confirmations or enhance security by increasing them, follow the recommendations below for an effective setup. ## When to reduce the number of confirmations A low number of blockchain confirmations should only be set for transactions coming from well-known and trusted sources. This is advisable when the origin of the transaction is your connected exchange account, a trusted counterparty, or even a transaction between two vault accounts within your workspace. ## When to increase the number of confirmations A low number of blockchain confirmations should *not* be set for transactions arriving from unknown external addresses, or from addresses that you do not control or do not have a well-established, trusted business relationship with. ## Fireblocks Deposit Control & Confirmation Policy The Deposit Control and Confirmation Policy allows you to specify the number of blockchain network confirmations required for incoming and outgoing transactions to clear, ensuring that their funds are credited to a wallet. After the transaction clears, its deposit amount is accounted for in the wallet’s currently available balance. Additionally, for UTXO-based blockchain networks, the transaction's outputs become immediately spendable. ### Default Confirmation Policy Each workspace in Fireblocks has a default configuration policy that specifies the number of confirmations required for each blockchain to complete transactions and make the funds available in the wallet. To learn about the default values for each supported blockchain, refer to this [Help Center article](https://support.fireblocks.io/hc/en-us/articles/29217252948892-Deposit-Control-and-Confirmation-Policy) ### Custom Confirmation Policy You can define your own confirmation policy based on your organization’s business needs. Learn more about the Custom Confirmation Policy in this [Help Center article](https://support.fireblocks.io/hc/en-us/articles/29217315805724-Build-a-custom-Deposit-Control-and-Confirmation-Policy). ## Dynamically overriding the required number of confirmations Fireblocks allows you to manually/dynamically confirm transactions and override your workspace's confirmation policy for specific transactions. This immediately updates the transaction status to Completed in your Fireblocks workspace. * To do so manually via the Console, refer to this [Help Center article](https://support.fireblocks.io/hc/en-us/articles/360015491419-Confirming-transactions-manually). * To do so via the API, refer to this [guide](/reference/settransactionconfirmationthreshold). # Developer Center Source: https://developers.fireblocks.com/docs/developer-center The Developer Center page lets your organization view and monitor your workspace's API activity in the Fireblocks Console. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/bd0e69d-developer-center-page-main.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=45b10a16343c467966048bb4b5b487b6) The **Overview** tab lets you view a bar graph showing the distribution of your workspace's API requests. The default view is a seven-day distribution, but you can toggle it to view the last 24 hours of activity in one-hour windows. Below the graph, you can view the category of each API request and what percentage that category makes up of the total distribution. Next to the percentage is the actual number of requests made within each category. # API monitoring The **API monitoring** tab lets you view a line graph of the types of requests made over the last seven days. Like on the Overview tab, you can switch the view to a 24-hour format divided into one-hour windows. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/40ad36c-developer-center-page-api-monitoring.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=2e5f4bbfba65949cb236df496c172957) Below the graph, a table shows a breakdown of each request by date and time, method, category, endpoint, response code, and the number of requests made. If your workspace has numerous requests, you can search the table using the search bar above it or filter the graph and the table using the Filters tree list on the left side of the page. You can filter by response code, method, and category. > [!NOTE] > > **Response code 429:** Response code 429, typically reserved for rate limited responses, is an upcoming feature that is not currently available to view or filter by. # API users > [!WARNING] > > **Only available to Admin-level workspace users.** The **API users** tab is only available to Admin-level workspace users such as the Owner, Admins, and Non-Signing Admins. The tab does not appear to any other user roles. The **API users** tab lets you create and manage your workspace's API users. A table shows a list of the workspace's API users by name, role, API key, what user group (or groups) they're in, and their current status. ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/2fc4d18-developer-center-apiuser.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=077748a99e7172187c5037dc646847b4) Visit our Help Center to learn more about: * [Adding API users](https://support.fireblocks.io/hc/en-us/articles/4407823826194-Adding-new-API-users) * [Re-enrolling API users](https://support.fireblocks.io/hc/en-us/articles/4412016177554-Re-enrolling-API-users) * [Deleting users](https://support.fireblocks.io/hc/en-us/articles/4404971260050-Deleting-Users) (Owner only) # Embedded Wallets Source: https://developers.fireblocks.com/docs/dynamic-embedded-wallets Build non-custodial wallet experiences with Dynamic's MPC-secured embedded wallets, now part of Fireblocks. [Dynamic](https://www.dynamic.xyz/), a Fireblocks company, is a developer platform powering millions of onchain user accounts across web, iOS, and Android. Dynamic's non-custodial embedded wallets let your users hold assets, earn yield, spend from cards, and access DeFi protocols directly inside your application, with no browser extension or seed phrase required. Try the [live demos](https://demo.dynamic.xyz/) to see it in action, or visit the [Dynamic documentation](https://www.dynamic.xyz/docs/overview/introduction/welcome) for SDK guides, platform docs, and recipes. ## Start building **1. Create an environment** Sign up and create an environment in the [Dynamic dashboard](https://app.dynamic.xyz/). **2. Install the Dynamic MCP** Connect your AI coding agent to Dynamic's documentation so it can assist with implementation. **Cursor** [Add the Dynamic MCP to Cursor.](https://cursor.com/en/install-mcp?name=dynamic\&config=eyJ1cmwiOiJodHRwczovL3d3dy5keW5hbWljLnh5ei9kb2NzL21jcCJ9) **Claude Code** ```bash theme={"system"} claude mcp add --transport http dynamic https://www.dynamic.xyz/docs/mcp ``` **Codex** ```bash theme={"system"} codex mcp add dynamic --url https://www.dynamic.xyz/docs/mcp ``` **Other** If your editor or agent supports remote HTTP MCP, add a server pointing at `https://www.dynamic.xyz/docs/mcp`. See your tool's MCP documentation for setup details. **3. Ask your agent** Your agent can now read Dynamic's full documentation and help you implement embedded wallets, configure authentication, and integrate onchain features. ## Key capabilities * **MPC security.** Dynamic wallets use Threshold Signature Schemes (TSS-MPC). The full private key never exists in one place, removing single points of failure even during signing. * **Sub-second signing.** Optimized MPC protocols sign transactions in under one second. * **Cross-chain support.** Native support for EVM, Solana, Bitcoin, Sui, and TON. Signer support for Cosmos, Aptos, Algorand, Tron, Starknet, and any chain using secp256k1, Ed25519, or BIP-340. * **Non-custodial by default.** Users own and control their keys. Key shares can be exported at any time to move to another provider. * **No seed phrases.** Multiple recovery and backup options replace traditional seed phrase flows. * **Smart accounts.** Turn embedded wallets into smart accounts to sponsor gas fees, add approval logic, and batch transactions. * **Multi-platform SDKs.** Ship on web (JavaScript, React), mobile (React Native, Swift, Flutter, Kotlin), and server (Node, Python). ## How it works Dynamic embedded wallets use a 2-of-2 TSS-MPC architecture. One key share stays on the user's device; the other participates in signing operations within a Trusted Execution Environment (TEE). Neither share is a usable key on its own, and the full private key is never constructed. For added security, the threshold can be raised to 2-of-3 with additional backup shares. For a deeper look at the MPC architecture, key sharing models, and recovery flows, see [Dynamic's embedded wallet overview](https://www.dynamic.xyz/docs/overview/wallets/embedded-wallets/mpc/overview). ## Explore the docs > **[React quickstart](https://www.dynamic.xyz/docs/javascript/reference/react-quickstart)** > > Get up and running with Dynamic in a React app. > **[Agents overview](https://www.dynamic.xyz/docs/overview/agents/overview)** > > Server wallets, agent wallets, and delegated access patterns for AI agents. > **[Live demos](https://demo.dynamic.xyz/)** > > Try embedded wallets, trading, yield, and payments in a live environment. > **[API reference](https://www.dynamic.xyz/docs/api-reference)** > > Explore Dynamic's REST API for server-side wallet and user management. [Contact us](https://fireblocks.com/dynamic) for a detailed walkthrough or to discuss your use case. ## Existing Fireblocks embedded wallet users > [!NOTE] > > The legacy Fireblocks Embedded Wallet APIs and SDKs remain fully supported. For legacy documentation, see [Legacy Embedded Wallets](/docs/embedded-wallet-introduction). For guidance on moving to Dynamic, see [Dynamic's migration guide](https://www.dynamic.xyz/docs/overview/wallets/embedded-wallets/mpc/migrate-from-fireblocks). # Elliptic Source: https://developers.fireblocks.com/docs/elliptic Connect Elliptic for AML/KYT screening: what Fireblocks sends, the risk score returned, timeouts, and supported assets. Elliptic is an AML/KYT provider. It is a premium add-on that Fireblocks Support enables on Mainnet workspaces, and you connect it in the Console; there is no API for connecting. Through the API you read its policy and results the same way as for any AML/KYT provider; see [AML/KYT Overview](/docs/aml-kyt-overview). ## What is sent and returned Fireblocks sends transaction hash (incoming) or destination address (outgoing), asset and blockchain, and the [Customer Reference ID](/docs/customer-reference-id). Elliptic returns a numeric risk score. In `GET /screening/transaction/{txId}`, the `risk` field usually carries the score but can also carry categorical values such as `noRiskDetected`, so do not assume it is always numeric. Outcome (Post-Screening Policy) actions: Accept, Reject, Alert. ## Connect You need an Elliptic API key and secret; if you can't create them, contact your Elliptic account manager. Enter both in the Console; default Trigger (Screening Policy) and Outcome rules apply until you create your own. To switch to new credentials, submit a ticket to Fireblocks Support. ## Timeouts | Direction | Wait | Resolved by | | - | - | - | | Incoming | Up to 10 minutes | Transaction hash, after a number of blockchain confirmations | | Outgoing | Up to 1 minute | Address | If no result arrives in time, `bypassScreeningDuringServiceOutages` decides what happens; see [Outage and delay settings](/docs/compliance-policies#outage-and-delay-settings). ## Supported assets Elliptic supports blockchains broadly, including cross-chain activity such as bridges and swaps. Newly deployed tokens are usually supported automatically. ## Limitations * Mainnet only; Testnet assets are not supported, so you cannot test Elliptic screening on a Testnet workspace. * Rejection details are available only from Fireblocks, not from Elliptic. * Only Elliptic's total risk score is used; source and destination sub-scores are not rule conditions. A null score is recorded as `noRiskDetected`. ## On the Help Center To connect Elliptic or test your rules in the Console, see [Elliptic](https://support.fireblocks.io/hc/en-us/articles/30614362937372-Elliptic). # Enable the Gas Station Source: https://developers.fireblocks.com/docs/enabling-the-gas-station-1 The Fireblocks Gas Station automates fueling vault accounts with the base asset required to pay transaction fees on Ethereum, other Ethereum Virtual Machine (EVM) blockchains, and TRON. This page covers how to enable the Gas Station in your workspace and designate its tank. The Gas Station tank is a funding source inside your own workspace: one of your vault accounts, or a [Wallet Pool](/docs/wallet-pools#use-cases). You hold the base assets in the tank, and the Gas Station transfers them from the tank to the vault accounts that have [auto-fueling](/docs/set-auto-fueling-property) turned on. ## Enable the Gas Station 1. [Contact Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=360003372200) to enable the Gas Station in your workspace. 2. After Fireblocks Support enables it, go to **Settings** and confirm that the **Gas Station tank** section is available. ## Designate the Gas Station tank 1. In **Settings** > **Gas Station tank**, choose a vault account or a Wallet Pool as the funding source. 2. Optionally, select **Automatically boost stuck transactions** so that fueling transactions stuck on an EVM network are replaced with a higher fee. 3. [Fund the tank](/reference/fund-the-gas-station) with the base asset of each network you want to fuel. 4. [Turn on auto-fueling](/docs/set-auto-fueling-property) for the vault accounts the tank should fuel. For detailed setup instructions, see [Setup for Gas Station 2.0](https://support.fireblocks.io/hc/en-us/articles/11602800877596-Setup-for-Gas-Station-2-0). # EVM Gasless (Universal Gasless) Source: https://developers.fireblocks.com/docs/evm-gasless Send EVM token transfers and contract calls from vault accounts with no ETH. Set up a relay and Policy rules, send with useGasless, and read gaslessInfo. ## Overview New to EVM Gasless? See [Universal Gasless](https://support.fireblocks.io/hc/en-us/articles/19948199000092) in the Help Center for a product introduction, then use this guide for the setup and API behavior. EVM Gasless, also known as Universal Gasless, lets a vault account on an EVM chain send tokens and call contracts without holding the native asset. The vault account signs a meta-transaction, and a relay vault account submits it on-chain and pays the gas. It supports any ERC-20, ERC-721, or ERC-1155 token on the [Universal Gasless integrated chains](https://support.fireblocks.io/hc/en-us/articles/21285504712988). It does not relay native asset transfers such as ETH. For how gasless compares across chains, see the [Gasless Transactions Overview](/docs/gasless-transactions). ## How it works EVM Gasless uses EIP-7702 to upgrade a vault account's address into a smart contract wallet that accepts relayed calls. The upgrade happens once per vault account per chain, during that account's first gasless transaction. That first transaction carries one additional action (the delegation), and later transactions from the same account are single meta-transactions. The upgrade does not change the vault account's address, keys, or balances. After the upgrade, the Console shows an indicator next to the vault account. ```mermaid theme={"system"} flowchart LR A[Choose a relay] --> B[Configure in the Console] B --> C[Add Policy rules] C --> D[Send a transaction] D --> E[Confirm the relay paid] ``` ### Smart contracts The upgrade delegates the vault account to the Universal Gasless Delegate contract. A separate KillSwitch contract can pause the Delegate in response to a security incident. Both contracts are deployed at the same addresses on every supported chain. | Contract | Address | | - | - | | Universal Gasless Delegate | [`0x0000Fb7702036ff9f76044a501ac1aA74cbab16b`](https://etherscan.io/address/0x0000Fb7702036ff9f76044a501ac1aA74cbab16b#code) | | KillSwitch | [`0x00000000FB2736C301E53904409f03De06c8467A`](https://etherscan.io/address/0x00000000FB2736C301E53904409f03De06c8467A#code) | * **Open source.** The contract source code is public on GitHub at [fireblocks/universal-gasless-7702-delegate](https://github.com/fireblocks/universal-gasless-7702-delegate). * **Verified on Etherscan.** The deployed source code is verified on Etherscan, so you can confirm that the on-chain contracts match the published source. * **Audited.** OpenZeppelin and Certora have audited the contracts. Audit reports are available on request through your Customer Success Manager. ## Choose a relay The relay is the vault account that pays gas for your gasless transactions. | Console option | Who pays gas | Setup | | - | - | - | | **This workspace** | A vault account in your own workspace | Self-serve. You keep the relay vault account funded with the native asset. | | **External workspace** | A vault account in another Fireblocks workspace | Requested through your Customer Success Manager and approved by the other workspace's Admin. | | **Fireblocks** | A Fireblocks-operated relay | Paid add-on. See [Using the Fireblocks Gasless Relay](https://support.fireblocks.io/hc/en-us/articles/23508430639516). | ### Use a Wallet Pool as the relay A single relay vault account has one nonce stream, so it becomes a bottleneck as gasless volume grows. With **This workspace** as the relay, you can select a [Wallet Pool](/docs/wallet-pools) instead of a single vault account, and Fireblocks spreads relaying across the pool's members. A relay pool needs more native-asset headroom per member than a transfer pool, because the first transaction from each vault account also pays for the EIP-7702 upgrade. See [Wallet Pools use cases](/docs/wallet-pools#use-cases). ### Relay for other workspaces To pay gas for other workspaces, go to **Settings** > **Become a gasless relayer**. A relay workspace must use an [API Co-signer](/cosigners/overview/cosigner-architecture-overview) to sign relay transactions. See [Configuring your workspace as a relay provider](https://support.fireblocks.io/hc/en-us/articles/14773041139868). ## Step 1. Configure EVM Gasless in the Console Go to **Settings** > **Initiate gasless transactions**, turn on **EVM**, select **Universal gasless** and the relay, then select the default mode. See [Configuring Universal Gasless transactions](https://support.fireblocks.io/hc/en-us/articles/19948348855964) for the full steps. | Mode | Behavior | | - | - | | **On by default** | Eligible transactions are gasless unless the request sets `useGasless: false`. | | **Off by default** | Transactions pay their own fee unless the request sets `useGasless: true`. | | **None** | Gasless is off for every vault account and cannot be requested per transaction. | ## Step 2. Add Policy rules EVM Gasless adds transactions that your Policy must allow: | Rule | Where | Needed for | | - | - | - | | **Vault account upgrade**, source = the vault accounts that send gasless, action = Allow | Your workspace | Every relay option | | **Contract call**, initiator = **Gasless-Orchestrator**, designated signer = **Single user** or **Multiple users** | The relay's workspace | **This workspace** as the relay | For the Contract call rule: * **Initiator.** Select **Gasless-Orchestrator** explicitly. This rule is required even if you already have a Contract call rule whose initiator is *any*, because a rule that generalizes the initiator does not cover Gasless-Orchestrator. * **Designated signer.** Gasless-Orchestrator cannot sign, so the designated signer cannot be the initiator. Set **Designated signer** to **Single user** or **Multiple users**, not **Initiator**. ## Step 3. Send a transaction Submit a standard transfer with [Create a new transaction](/reference/createtransaction). When the mode is **On by default**, you need no extra fields. `POST /v1/transactions` ```jsonc theme={"system"} { "assetId": "USDC", "amount": "250", "source": { "type": "VAULT_ACCOUNT", "id": "42" }, "destination": { "type": "ONE_TIME_ADDRESS", "oneTimeAddress": { "address": "0x..." } }, "useGasless": true // Optional - Overrides the workspace default for this transaction } ``` **Field notes:** * `useGasless`: set `true` to request gasless when the mode is **Off by default**, or `false` to pay the fee directly when it is **On by default**. Omit it to follow the workspace default. It has no effect when the mode is **None**. * `source`: the vault account needs the token balance only. It does not need the native asset. * `assetId`: a token on a supported chain. Native asset transfers are not relayed. ## Step 4. Confirm the relay paid [Get a transaction](/api-reference/transactions/get-a-specific-transaction-by-fireblocks-transaction-id) or read the [transaction webhook](/reference/monitoring-transaction-status). A gasless transaction carries `gaslessInfo`, and `feeInfo` reports who paid. ```json theme={"system"} { "id": "5c1f...", "status": "COMPLETED", "assetId": "USDC", "gaslessInfo": { "isMetaTx": true, "relayTenantId": "8a2e...", "relayTenantName": "Acme Treasury", "relayVaultAccountId": "7" }, "feeInfo": { "paidByRelay": true, "relayType": "LOCAL", "relayId": "7" } } ``` | Field | Description | | - | - | | `gaslessInfo.isMetaTx` | `true` when the transaction was relayed. `gaslessInfo` is returned only for relayed transactions. | | `gaslessInfo.relayVaultAccountId` | The relay vault account that paid the gas. | | `feeInfo.paidByRelay` | `true` when the relay paid the fee. | | `feeInfo.relayType` | `LOCAL` for a relay in your workspace, `THIRD_PARTY` for an external workspace or the Fireblocks relay. | | `feeInfo.relayName` | The relay's workspace name. Returned for `THIRD_PARTY` relays only. | For relayed transactions, the deprecated top-level `fee` field is omitted. Read `feeInfo` instead. ## Troubleshooting | Symptom | Cause | Fix | | - | - | - | | Error `1455` "Missing Gasless configuration" | Gasless is not set up for this workspace or asset | Configure a relay in the Console, or send without `useGasless: true`. | | First transaction from a vault account is blocked by Policy | No **Vault account upgrade** rule covers the account | Add the rule from [Step 2](#step-2-add-policy-rules). | | Relay transaction is blocked by Policy in the relay workspace | The Contract call rule does not name Gasless-Orchestrator, or its designated signer is **Initiator** | Fix the relay workspace's rule as in [Step 2](#step-2-add-policy-rules). | See [API error codes](/reference/api-error-codes) for the full list. ## Related * [Gasless Transactions Overview](/docs/gasless-transactions): compare gasless mechanisms across chains. * [Wallet Pools](/docs/wallet-pools): spread relaying across several vault accounts. * [Sweep to omnibus](/reference/sweep-to-omnibus-1#fueling): use EVM Gasless to sweep tokens from deposit vault accounts. * [Work with Gas Station](/docs/work-with-gas-station): fund vault accounts for native asset transfers. # Exchanges Travel Rule Overview Source: https://developers.fireblocks.com/docs/exchanges-travel-rule Send encrypted Travel Rule PII with exchange deposits and withdrawals through the Fireblocks API. Binance, Bitstamp, Bitfinex, OKX, and TrustCo require the originator's or beneficiary's personal data (PII) for some deposits to and withdrawals from your account with them. It does not go through a Travel Rule provider or your Travel Rule policy: Fireblocks passes it to the exchange, which applies its own requirements. The API is available to all customers; the Console flow is opt-in through your Customer Success Manager. ## What you can do through the API | Task | Console | API | | - | - | - | | Get the workspace exchange public key | No | [Get public key to encrypt exchange credentials](/api-reference/exchange-accounts/get-public-key-to-encrypt-exchange-credentials) | | Send Travel Rule data with a transaction | Opt-in | `extraParameters.piiData` in [`POST /transactions`](/api-reference/transactions/create-a-new-transaction) | ## Supported venues Each venue's guide lists the fields it requires for every direction and scenario. | Venue | When PII is required | Requirements | | - | - | - | | [Binance](/docs/binance-travel-rule) | Withdrawals and deposits | Vary by jurisdiction | | [Bitstamp](/docs/bitstamp-travel-rule) | Withdrawals and deposits | Fixed field combinations | | [Bitfinex](/docs/bitfinex-travel-rule) | Withdrawals | Fixed fields | | [OKX](/docs/okx-travel-rule) | Withdrawals | Fixed fields | | [TrustCo](/docs/trustco-travel-rule) | Withdrawals | Fixed fields | TrustCo is a custodian, not an exchange, and works the same way. It is unrelated to [TRUST](/docs/interact-with-trust), the Travel Rule network. ## Step 1: Find the fields to send Answer these in order, then find the matching row in the venue's guide: 1. **Venue:** Binance, Bitstamp, Bitfinex, OKX, or TrustCo. 2. **Direction:** withdrawal from the exchange, or deposit to it. 3. **Jurisdiction (Binance only):** the country of your Binance entity, sent as `vaspCountry`. 4. **Relationship:** the other party is you (`FirstParty`) or someone else (`ThirdParty`). 5. **Counterparty wallet:** a private (unhosted) wallet, or an account at another VASP. 6. **Entity type:** `Individual` or `Business`. ## Step 2: Build the piiData payload ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": {}, "originator": {}, "beneficiaryVASP": {}, "originatingVASP": {}, "transactionData": {} } } ``` `type` and `typeVersion` stay in plain text; everything inside `data` is encrypted in Step 3. | Direction | Party fields | VASP fields | | - | - | - | | Withdrawal from the exchange | `beneficiary` | `beneficiaryVASP`, and `originatingVASP` where required | | Deposit to the exchange | `originator` | `originatingVASP`, and `beneficiaryVASP` where required | **Party fields** | Field | Description | | - | - | | `participantRelationshipType` | `FirstParty` if the party is you; `ThirdParty` if it is someone else. | | `entityType` | `Individual` for a person; `Business` for a legal entity. | | `names` | Array. For individuals: `primaryName`, `secondaryName`, and `nameType` (for example `Latin`). For Japanese Kana and Kanji: `primaryName` and `nameType` (`Kana` or `Kanji`). | | `company.name` | For businesses. | | `nationalIdentification` | `nationalIdentifierType` (for example `PASSPORT` or `ABN`) and `nationalIdentifier`. | | `registrationNumber` | A business's registration number, where required. | | `dateOfBirth` | For individuals, where required. | | `postalAddress` | `country` (ISO 2-letter code), `city`, `streetName`, `buildingNumber`, `postalCode`, `subdivision`. | | `isHosted` | OKX: whether the destination wallet is hosted. | | `externalReferenceId` | TrustCo: your reference for the party. | **VASP fields** | Field | Description | | - | - | | `vaspCode` | The counterparty VASP. Format differs by venue: a code such as `BINANCE`, a UUID for Bitstamp, or a DID for Bitfinex and OKX. | | `vaspName` | The counterparty VASP's name. Used for Binance deposits, and instead of `vaspCode` for TrustCo and for VASPs a venue does not list. | | `vaspRegion` | The VASP's region, where needed. | **Transaction fields (Binance)** | Field | Description | | - | - | | `transactionData.withdraw.isAddressVerified`, `transactionData.deposit.isAddressVerified` | Whether the withdrawal or deposit address was verified. | | `transactionData.withdraw.txPurpose`, `transactionData.deposit.txPurpose` | The transaction's purpose, in jurisdictions that require it. | For a complete plaintext example per scenario, see the venue's guide. ## Step 3: Encrypt the values Exchanges accept RSA only; hybrid encryption (RSA with AES) is rejected. * **Algorithm:** RSA-OAEP with SHA-256. * **Scope:** encrypt each value in `data` individually, keeping the object structure; every value becomes a base64 ciphertext. * **Key:** your workspace exchange public key. It is workspace-wide, not tied to an exchange, and is the same key that encrypts exchange credentials. ```ts theme={"system"} const { data } = await fireblocks.exchangeAccounts.getExchangeAccountsCredentialsPublicKey(); const publicKey = data.publicKey; ``` ```ts theme={"system"} /** * Cleans PEM headers, footers, and whitespace, then converts it to an ArrayBuffer. */ function pemToArrayBuffer(pem: string): ArrayBuffer { const cleanBase64 = pem .replace(/-----BEGIN PUBLIC KEY-----/g, '') .replace(/-----END PUBLIC KEY-----/g, '') .replace(/\s+/g, ''); const binaryString = atob(cleanBase64); return Uint8Array.from(binaryString, (char) => char.charCodeAt(0)).buffer; } /** * Imports an RSA public key from PEM format for the Web Crypto API. */ export async function importRsaPublicKey(pemContents: string): Promise { const binaryDer = pemToArrayBuffer(pemContents); return crypto.subtle.importKey( 'spki', binaryDer, { name: 'RSA-OAEP', hash: 'SHA-256' }, true, ['encrypt'] ); } /** * Encrypts a primitive value using an imported CryptoKey (RSA-OAEP with SHA-256) * and returns a Base64 string. */ async function encryptPrimitiveData(data: string | number | boolean, cryptoKey: CryptoKey): Promise { const encodedData = new TextEncoder().encode(String(data)); const encryptedBuffer = await crypto.subtle.encrypt( { name: 'RSA-OAEP' }, cryptoKey, encodedData ); return Buffer.from(encryptedBuffer).toString('base64'); } /** * Encrypts each PII field individually using RSA-OAEP encryption. * Preserves the original object/array structure. * * @param piiData - PII data object to encrypt field by field * @param publicKey - RSA public key in PEM format or JSON string containing credPublicKey * @returns Promise - Object with each primitive field encrypted individually in Base64 */ export async function encryptPiiFieldsIndividually(piiData: any, publicKey: string): Promise { // 1. Extract PEM content if JSON string was provided, then import key once const pemContents = publicKey.trim().startsWith('{') ? JSON.parse(publicKey).credPublicKey : publicKey; const cryptoKey = await importRsaPublicKey(pemContents); // 2. Recursive traversal helper async function encryptPrimitives(obj: any): Promise { if (obj === null || obj === undefined) { return obj; } if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') { return await encryptPrimitiveData(obj, cryptoKey); } if (Array.isArray(obj)) { return Promise.all(obj.map((item) => encryptPrimitives(item))); } if (typeof obj === 'object') { const result: any = {}; for (const [key, value] of Object.entries(obj)) { result[key] = await encryptPrimitives(value); } return result; } return obj; } // 3. Execute recursive encryption return encryptPrimitives(piiData); } ``` ## Step 4: Create the transaction ```ts theme={"system"} const encryptedPiiData = await encryptPiiFieldsIndividually(piiData, publicKey); const { data: tx } = await fireblocks.transactions.createTransaction({ transactionRequest: { operation: "TRANSFER", assetId: "BTC", amount: "100", source: { type: "VAULT_ACCOUNT", id: "1" }, destination: { type: "EXCHANGE_ACCOUNT", id: "exchange_account_id" }, extraParameters: { piiData: encryptedPiiData }, }, }); console.log("Exchange transaction created:", tx.id); ``` ## Before you go live * Map your customer data to the fields each venue requires. * Check that the plaintext PII matches the KYC records you hold. * Test in a sandbox workspace. ## Limitations * **Fireblocks does not validate `piiData`.** The API accepts any payload; if the exchange requires data that is missing or incomplete, the transaction may fail. * **Never send unencrypted PII** in an API call. ## On the Help Center To provide Travel Rule data for exchange transactions in the Console, see [Exchanges Travel Rule Overview](https://support.fireblocks.io/hc/en-us/articles/30614376019484-Exchanges-Travel-Rule-Overview). # Execute Smart Transfers Source: https://developers.fireblocks.com/docs/execute-smart-transfers As you can open a regular Smart Transfer ticket [via these API endpoints](/reference/createticket), an intermediary can open a Smart Transfer ticket for two third-parties only via our API as well, and follow its life cycle. In order for you, as an intermediary, to open such a ticket for your third-parties, all three of you need to be connected to each other with the same network profiles on the Fireblocks Network. # Smart Transfer flow To create Smart Transfer tickets an established network connection must exist. In the intermediary case, all sides (intermediary and counterparties settling the tickets) should be connected via the same Network Profile. The flow of executing a Smart Transfer ticket consists of the following steps: 1. [Create a Smart Transfer ticket](/reference/createticket) 2. [Fund Smart Transfer ticket](/reference/fulfillticket) 3. [Cancel Smart Transfer ticket](/reference/cancelticket) For more details, see the [Smart Transfers Developer Guide](/reference/execute-smart-transfers-1). # Export a Proof of Ownership Document Source: https://developers.fireblocks.com/docs/export-proof-of-ownership Create a Proof of Ownership PDF for an address your workspace owns through the Fireblocks API. A proof of ownership document is a PDF confirming that a blockchain address belongs to your legal entity, for a regulator, auditor, bank, or counterparty. You can create one for any address registered to your own legal entity. It uses Address Registry data, so your workspace must participate in [Address Registry](/reference/address-registry). ## What you can do through the API | Task | Console | API | | - | - | - | | Create a proof of ownership PDF | Yes | [`POST /address_registry/proof_of_ownership_exports`](/api-reference/compliance/create-a-proof-of-ownership-pdf-for-an-address) | ## Step 1: Check the address Look up the address and check `proofOfOwnershipAvailable` in the response. When it is `true`, creating the document is likely to succeed. See [Look up an address](/reference/address-registry#look-up-an-address). ## Step 2: Create the document Send the address. It must resolve to your workspace, in the same format as the lookup endpoint. The request accepts an optional `Idempotency-Key` header, valid for 24 hours. ```json theme={"system"} { "address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb0" } ``` ```ts theme={"system"} const { data } = await fireblocks.compliance.createAddressRegistryProofOfOwnership({ addressRegistryCreateProofOfOwnershipRequest: { address: "0x742d35cc6634c0532925a3b844bc9e7595f0beb0", }, }); fs.writeFileSync("proof-of-ownership.pdf", Buffer.from(data.pdf, "base64")); ``` The PDF is returned base64-encoded in `pdf`. Save the PDF as soon as you receive it. Fireblocks does not store it, and there is no way to download it again. ## Errors | Status | Meaning | What to do | | - | - | - | | `400` | The address is missing, or your workspace is not opted in to Address Registry. | Check the request and your participation status. | | `403` | Your workspace is blocked from creating proof of ownership documents. | Contact Fireblocks Support. | | `404` | The address is not found, not owned by your workspace, or not usable for a document. | Check `proofOfOwnershipAvailable` first. | | `429` | Rate limited. | Retry after backing off. | | `500` | Internal error; no PDF is returned. | Do not retry automatically; contact Fireblocks Support if it persists. | | `503` | Temporarily unavailable. | Retry after backing off. | `500` and `503` return the same error code; use the HTTP status to tell them apart. ## Limitations * **Your own addresses only:** the address must be registered to your own legal entity. * **No re-download:** Fireblocks does not store the PDF. * Address Registry limitations apply: not available on European or Swiss-hosted workspaces, or on Developer Sandbox workspaces. ## On the Help Center To export a proof of ownership document in the Console, see [Export a Proof of Ownership Document](https://support.fireblocks.io/hc/en-us/articles/30614316537884-Export-a-Proof-of-Ownership-Document). # Fetching Transaction Receipt Source: https://developers.fireblocks.com/docs/fetching-transaction-receipt This guide outlines the procedure for retrieving a transaction receipt via the endpoint, utilizing the function in the Fireblocks SDK. By employing the `getTransactionReceipt` function of the Fireblocks SDK, you can obtain the receipt of any transaction that has been confirmed by the blockchain. This function takes in the `baseAssetId` (indicating the blockchain) and the `txHash` (indicating the transaction hash). It manages the process of querying the blockchain and returning the transaction receipt. > [!NOTE] > > This functionality is exclusively available for EVM (Ethereum Virtual Machine) compatible chains. ## Prerequisites Before fetching a transaction receipt, ensure you have the following information: 1. **Base Asset ID**: The `baseAssetId` of the blockchain where the transaction was executed (e.g., ETH for Ethereum). This is the Fireblocks ID of the blockchain's gas token. (To obtain the asset ID, refer to this [assetId list](https://support.fireblocks.io/hc/en-us/articles/8993656344092-Supported-blockchain-networks)). 2. **Transaction Hash**: The `txHash` of the transaction for which you wish to fetch the receipt. This is the unique identifier of the transaction on the blockchain and is a hex string (i.e. prefixed with 0x). You can obtain the transaction hash from the transaction details or the blockchain explorer. ## Example: Fetching a Transaction Receipt Below is an example of how to fetch a transaction receipt using the Fireblocks SDK: ### 1. Initialize the Fireblocks SDK First, import the Fireblocks SDK and initialize it with your Fireblocks API key and private key: ```typescript TypeScript theme={"system"} import { Fireblocks, BasePath } from '@fireblocks/ts-sdk'; const privateKey = '...'; // Your Fireblocks API private key const apiKey = '...'; // Your Fireblocks API key const fireblocksSdk = new Fireblocks({ apiKey, basePath: BasePath.US, // Use BasePath.EU for the EU region secretKey: privateKey, }); ``` ### 2. Fetch the Transaction Receipt To fetch the transaction receipt, utilize the `getTransactionReceipt` function of the Contract Interactions Controller: ```typescript TypeScript theme={"system"} const baseAssetId = 'ETH'; // The base asset ID for Ethereum const txHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; // The transaction hash const receipt = await fireblocks.getTransactionReceipt(baseAssetId, txHash); console.log(receipt); ``` The response will be a transaction receipt object containing details about the transaction, such as the status, gas used, logs, and more. For further information about the transaction receipt object, refer to the [Endpoint Model](#endpoint-model) section. ## Endpoint Model ### Transaction Receipt The transaction receipt response object includes the following fields: * `status`: The status of the transaction (e.g., success or failure). * `gasUsed`: The amount of gas consumed by the transaction. * `logs`: The logs generated by the transaction (if any). * `transactionHash`: The hash of the transaction. * `blockHash`: The hash of the block containing the transaction. * `blockNumber`: The number of the block containing the transaction. * `contractAddress`: The address of the contract created (if any). * `cumulativeGasUsed`: The cumulative gas used by the transaction. * `from`: The address of the sender. * `to`: The address of the receiver. * `effectiveGasPrice`: The actual price per unit of gas paid. * `type`: The type of the transaction. * `logs`: The logs generated by the transaction (if any). * `logsBloom`: The bloom filter for the logs of the transaction. * `transactionIndex`: The index of the transaction in the block. > [!NOTE] > > The `logs` field contains an array of log objects, each representing a log generated by the transaction. Each log object includes the following fields: > > * `address`: The address of the contract that generated the log. > * `topics`: An array of topics associated with the log. > * `data`: The data contained in the log. > * `blockNumber`: The number of the block containing the log. > * `transactionHash`: The hash of the transaction that generated the log. > * `transactionIndex`: The index of the transaction in the block. > * `blockHash`: The hash of the block containing the log. > * `logIndex`: The index of the log in the block. > * `removed`: Indicates whether the log was removed. > [!NOTE] > > Some fields may be empty in the response if the transaction has not been executed or if the blockchain does not support them. # Fireblocks CLI Source: https://developers.fireblocks.com/docs/fireblocks-cli Execute any Fireblocks API operation from the command line. The Fireblocks CLI (`@fireblocks/fireblocks-cli`) is an agent-first command-line tool that exposes every Fireblocks API operation as a typed command. Commands are organized by API namespace (e.g. `vaults`, `transactions`, `embedded-wallets`) and use JWT-signed requests identical to the Fireblocks SDKs. The CLI is built for: * **Scripting and automation** — run API calls from shell scripts, CI pipelines, or cron jobs * **Exploration** — inspect vault balances, transaction status, or workspace state without writing code * **AI agent integration** — the `help-index` command returns a compact JSON index of all commands that fits in an LLM context window ## Features * Every Fireblocks API endpoint as a typed command * JWT-signed requests using the same signing approach as the TypeScript SDK * Credential management — store profiles in `~/.config/fireblocks/config.json` * `--dry-run` — preview any request before executing it * `--debug` — log request and response details to stderr * Write-operation confirmation prompts (skippable with `--no-confirm`) * Structured JSON errors on stderr with distinct exit codes * Shell autocomplete for Bash and Zsh ## Installation The CLI is available as an npm package and requires Node.js 18 or later. * **npm package**: `https://www.npmjs.com/package/@fireblocks/fireblocks-cli` * **GitHub repo**: `https://github.com/fireblocks/fireblocks-cli` * **GitHub release (latest)**: `https://github.com/fireblocks/fireblocks-cli/releases/latest` ### npm (recommended) ```bash theme={"system"} npm install -g @fireblocks/fireblocks-cli ``` ### Standalone installers (macOS, Windows, Linux) Standalone installers for each OS are available in the GitHub Releases assets. ### Tarballs Tarballs are also available in the GitHub Releases assets. ### Homebrew (macOS) ```bash theme={"system"} brew tap fireblocks/fireblocks-cli brew install fireblocks-cli ``` ### Other package managers ```bash theme={"system"} # yarn yarn global add @fireblocks/fireblocks-cli # pnpm pnpm add -g @fireblocks/fireblocks-cli ``` The `fireblocks` command will be available immediately after installation. ## Verify Installation ```bash theme={"system"} fireblocks --version ``` ## Quick Start ```bash theme={"system"} # 1. Set up credentials interactively fireblocks configure # 2. Verify credentials fireblocks whoami # 3. List vault accounts fireblocks vaults get-paged-vault-accounts # 4. Discover all available commands fireblocks help-index ``` ## Next Steps * [Authentication](./cli-authentication) — configure credentials, environment variables, and profiles * [Usage Guide](./cli-usage) — commands, flags, examples, and exit codes # Gas Management Overview Source: https://developers.fireblocks.com/docs/gas-management Stop funding each vault account with gas by hand. Compare gasless transactions, where a relay pays the fee, with Gas Station, which auto-fuels vault accounts. ## Overview Every on-chain transaction pays a network fee in the chain's native asset, such as ETH on Ethereum or SOL on Solana. By default, the source vault account pays that fee, so it must hold the native asset alongside the tokens it sends. Funding many vault accounts with gas by hand is slow and error-prone. Fireblocks gives you two ways to remove that work. | Tool | What it does | Use it when | | - | - | - | | [**Gasless transactions**](/docs/gasless-transactions) | A relay vault account pays the fee, so the source vault account holds no native asset | You send tokens on EVM or Solana | | [**Gas Station**](/docs/work-with-gas-station) | Tops up a vault account with the native asset when its balance falls below a [threshold you set](/docs/configure-gas-station-values) | Gasless does not cover your chain, or vault accounts send the native asset | ## Which should you use? For most use cases, use gasless transactions. Vault accounts send tokens without holding the native asset, so there is nothing to pre-fund. Use Gas Station for chains gasless does not cover, such as TRON, and for vault accounts that send the native asset itself. ## How they differ They differ in whether the native asset ever reaches the source vault account. | | Gasless transactions | Gas Station | | - | - | - | | How the fee is paid | A relay vault account pays it on the source's behalf | The source pays it, after Gas Station tops it up | | Native asset in source vault accounts | Not needed | Needed, and refilled automatically | | Native asset transfers | Not covered | Covered | | Chains | [EVM](/docs/evm-gasless) (EIP-7702) and [Solana](/docs/solana-gasless) (fee payer) | EVM and TRON | | Setup | **Settings** > **Initiate gasless transactions** | **Settings** > **Gas Station tank** | You can use both: gasless transactions for token transfers, and Gas Station for the vault accounts that also send the native asset. ## Scale the account that pays A gasless relay and a Gas Station tank are each a single vault account that pays for many others. On EVM, each vault account has one nonce stream, so that account becomes a bottleneck during traffic spikes. To spread the load, select a [Wallet Pool](/docs/wallet-pools#use-cases) as the relay or as the Gas Station tank. ## In this section * [Gasless Transactions Overview](/docs/gasless-transactions): how gasless works and which mechanism each chain uses. * [EVM Gasless (Universal Gasless)](/docs/evm-gasless): set up a relay and Policy rules, then send gasless on EVM chains. * [Solana Gasless](/docs/solana-gasless): set a fee payer and send SPL tokens without SOL. * [Work with Fireblocks Gas Station](/docs/work-with-gas-station): how Gas Station fuels vault accounts, and its use cases. ## Related * [Estimate Transaction Fee](/reference/estimate-transaction-fee): estimate fees and set custom fees through the API. * [Boost Transactions](/docs/boost-transactions): speed up a transaction that is stuck because its fee is too low. # Gasless Transactions Overview Source: https://developers.fireblocks.com/docs/gasless-transactions Send tokens from vault accounts that hold no native gas, with a relay that pays the fee. Compare the gasless mechanisms Fireblocks supports on EVM and Solana. ## Overview New to gasless transactions? See [About the Fireblocks Gasless Service](https://support.fireblocks.io/hc/en-us/articles/14771633894940) in the Help Center for a product introduction, then use these guides for the API behavior. Gasless transactions let a vault account send tokens without holding the chain's native asset. The vault account signs the transfer, and a separate *relay* vault account pays the network fee. You never fund the sending vault accounts with gas, which removes the top-up step that [Gas Station](/docs/work-with-gas-station) otherwise automates. Once your workspace is configured, gasless is applied automatically to eligible transactions. You submit a standard [Create a new transaction](/reference/createtransaction) request; no gasless-specific endpoint exists. Configuration happens in the Console only. The public API has no endpoint to set up relays. ```mermaid theme={"system"} sequenceDiagram participant You as Your API client participant FB as Fireblocks participant V as Source vault account participant R as Relay vault account participant C as Chain You->>FB: POST /v1/transactions FB->>V: Sign the transfer (no gas needed) FB->>R: Attach the relay R->>C: Submit and pay the fee C-->>You: Transfer lands, webhook fires ``` ## Choose a mechanism Fireblocks uses a different mechanism on each chain family. | Gasless type | Mechanism | Chains | Assets | Guide | | - | - | - | - | - | | **EVM Gasless** (Universal Gasless) | EIP-7702 | [Supported EVM chains](https://support.fireblocks.io/hc/en-us/articles/21285504712988) | Any ERC-20, ERC-721, or ERC-1155 token | [EVM Gasless (Universal Gasless)](/docs/evm-gasless) | | **Solana Gasless** | Solana fee payer | Solana | SPL tokens | [Solana Gasless](/docs/solana-gasless) | ## EVM and Solana at a glance The two chain families use different mechanisms, so their setup differs. | | EVM Gasless (Universal Gasless) | Solana Gasless | | - | - | - | | Who pays | A relay in your workspace, another workspace, or Fireblocks | A fee payer vault account in your workspace | | [Wallet Pool](/docs/wallet-pools) as the relay | Supported | Not supported | | Default mode | On by default, off by default, or none | On whenever a fee payer is set | | Policy rules to add | Vault account upgrade, plus Contract call for your own relay | None specific to gasless | | First transaction per vault account | Upgrades the account through EIP-7702 | No extra step | | Source restriction | None | Must share a key set with the fee payer | | Response field | `gaslessInfo` and `feeInfo` | `feePayerInfo` | ## What gasless does not cover * **Native assets.** No mechanism relays the chain's base asset. To move ETH, SOL, or another base asset, the source vault account pays its own fee. Use [Gas Station](/docs/work-with-gas-station) to keep those vault accounts funded. * **API configuration.** Relays, fee payers, and modes are set in the Console, not through the public API. ## Related * [EVM Gasless (Universal Gasless)](/docs/evm-gasless): set up and send gasless transactions on EVM chains. * [Solana Gasless](/docs/solana-gasless): set up a fee payer and send SPL tokens without SOL. * [Work with Gas Station](/docs/work-with-gas-station): keep native-asset balances funded for transactions gasless does not cover. # Create a CSR for an API user Source: https://developers.fireblocks.com/docs/generate-a-csr-for-an-api-user # What is a CSR? A Certificate Signing Request (CSR) is a file that contains your public key and some identifying information. Fireblocks uses the CSR to generate your API user's public key, which Fireblocks then uses to verify and authenticate your API calls. # Before you begin Make sure you have the following: * Access to a machine where you can run OpenSSL or similar tooling * A secure location to store your private key # Step 1: Create the private key & CSR files ## Using OpenSSL (recommended for most users) Open your terminal or command line and run the following command to generate a private key and CSR: ```bash Shell theme={"system"} openssl req -new -newkey rsa:4096 -nodes -keyout api_private.key -out api_user.csr ``` ### What this does * Creates a private key file: **api\_private.key** * Creates a CSR file: **api\_user.csr** > [!WARNING] > > **Security reminder:** *Never* upload or share the **api\_private.key** file. Store it securely using a Key Management System (KMS), HSM, or encrypted file storage. # Step 2: Fill in the CSR details When prompted, complete the following fields: | Field | Description | | - | - | | Common Name (CN) | Your name or the API user's name (e.g., John\_the\_API\_Guy) | | Organization (O) | Your organization's name | | Organizational Unit (OU) | Optional field (e.g., API Team) | | Country (C) | Two-letter country code (e.g., US) | | State (ST) | Your state or province | | Locality (L) | Your city | # Step 3: Upload the CSR file * In the Fireblocks Console, [follow these steps to create an API user](https://support.fireblocks.io/hc/en-us/articles/4407823826194-Adding-new-API-Users#h_01KRPE2ND7A53AD5R7TZYJ55C3). Note that only Admin-level workspace users (Owner, Admin, and Non-Signing Admin) can create API users. * Upload the **api\_user.csr** file in the **CSR File** field. > [!WARNING] > > Do *not* upload your private key! Keep the **api\_private.key** file secure for signing API requests later. # GTR Source: https://developers.fireblocks.com/docs/gtr Connect GTR through the Fireblocks API, then create, encrypt, and link Travel Rule messages and resolve missing or incomplete ones. Global Travel Rule (GTR) is a Travel Rule provider. It is a premium add-on that Fireblocks Support enables on Developer Sandbox, Testnet, and Mainnet workspaces. Everything, including connecting and creating Travel Rule messages (TRMs), happens through the API: you encrypt the PII on your side, and because Fireblocks does not hold the key that decrypts it, there is no Console flow. Rules are set by Fireblocks Support. ## What you can do through the API Paths marked `…` start with `/screening/trlink/customers/integration/{customerIntegrationId}`. | Step | Task | Console | API | | - | - | - | - | | 1 | Set up the Fireblocks SDK in your system | No | No API call; install the TypeScript SDK | | 2 | Create a Travel Rule legal entity | No | [`POST /screening/trlink/customers`](/api-reference/trlink/create-customer) | | 2 | List Travel Rule partners and their identifiers | No | [`GET /screening/trlink/partners`](/api-reference/trlink/list-available-trsupport-partners) | | 2 | Create the integration | No | [`POST /screening/trlink/customers/integration`](/api-reference/trlink/create-customer-integration) | | 2 | Connect with your GTR API key and secret | No | [`PUT /screening/trlink/customers/integration/{customerIntegrationId}`](/api-reference/trlink/connect-customer-integration) | | 2 | Test the connection | No | [`POST …/test_connection`](/api-reference/trlink/test-connection) | | 3 | Read the policy | No | [Get TRLink policy](/api-reference/trlink/get-trlink-policy) | | 3 | Change the policy | Through Fireblocks Support | No | | 4 | Encrypt the PII with GTR's open-source tool | No | No API call; see [GTR's Fireblocks integration guide](https://www.globaltravelrule.com/documentation/connect-fireblocks-with-gtr) | | 5, 6 | Assess whether a transaction needs a TRM | No | [`POST …/trm/assess`](/api-reference/trlink/assess-travel-rule-requirement) | | 5, 6 | Create a TRM | No | [`POST …/trm`](/api-reference/trlink/create-travel-rule-message) | | 5 | Create the transaction with the TRM attached | No | `travelRuleMessageId` in [`POST /transactions`](/api-reference/transactions/create-a-new-transaction) | | 5, 6 | Link a TRM to a transaction | No | [`POST /screening/trlink/transaction/{txId}/travel_rule_message_id`](/api-reference/trlink/set-transaction-travel-rule-message-id) | | 5 | Link a TRM to one destination | No | [`POST /screening/trlink/transaction/{txId}/destination/travel_rule_message_id`](/api-reference/trlink/set-destination-travel-rule-message-id) | | Resolve messages | Get a TRM's required actions | No | [Get required actions for a TRM](/api-reference/trlink/get-required-actions-for-a-trm) | | Resolve messages | Resolve a required action | No | [Resolve action for a TRM](/api-reference/trlink/resolve-action-for-a-trm) | | Resolve messages | Decide on a transaction that has no TRM | No | [`POST …/transactions/{txId}/manual_decision`](/api-reference/trlink/manual-decision-for-missing-trm) | | Resolve messages | Get a TRM's status and details | No | [`GET …/trm/{trmId}`](/api-reference/trlink/get-trm-by-id) | | Resolve messages | Cancel a TRM and notify the counterparty | No | [`POST …/trm/{trmId}/cancel`](/api-reference/trlink/cancel-travel-rule-message) | | Any time | Redirect a TRM to a subsidiary VASP | No | [`POST …/trm/{trmId}/redirect`](/api-reference/trlink/redirect-travel-rule-message) | | Any time | List or get VASPs in GTR's directory | No | [`GET …/vasps`](/api-reference/trlink/list-vasps), [`GET …/vasps/{vaspId}`](/api-reference/trlink/get-vasp-by-id) | | Any time | Check whether GTR supports an asset | No | [`GET …/assets`](/api-reference/trlink/list-supported-assets), [`GET …/assets/{assetId}`](/api-reference/trlink/get-supported-asset-by-id) | | Any time | List your Travel Rule legal entities | No | [`GET /screening/trlink/customers`](/api-reference/trlink/get-all-customers) | | Any time | Get a Travel Rule legal entity | No | [`GET /screening/trlink/customers/{customerId}`](/api-reference/trlink/get-customer-by-id) | | Any time | Update a Travel Rule legal entity | No | [`PUT /screening/trlink/customers/{customerId}`](/api-reference/trlink/update-customer) | | Any time | Delete a Travel Rule legal entity | No | [`DELETE /screening/trlink/customers/{customerId}`](/api-reference/trlink/delete-customer) | | Any time | List a legal entity's integrations | No | [`GET /screening/trlink/customers/{customerId}/integrations`](/api-reference/trlink/get-customer-integrations) | | Any time | Get an integration | No | [`GET /screening/trlink/customers/{customerId}/integrations/{customerIntegrationId}`](/api-reference/trlink/get-customer-integration-by-id) | | Any time | Disconnect | Through Fireblocks Support | [`DELETE /screening/trlink/customers/integration/{customerIntegrationId}`](/api-reference/trlink/disconnect-customer-integration) | ## Step 1: Set up the Fireblocks SDK Install the Fireblocks SDK in your system; TRMs are created and linked from your code. See [TypeScript SDK](/reference/typescript-sdk). Before you continue: * You have a GTR account with a valid subscription, an API key and secret, and your VASP's public key configured in the GTR Dashboard. See [GTR's Fireblocks integration guide](https://www.globaltravelrule.com/documentation/connect-fireblocks-with-gtr). * Fireblocks Support has enabled GTR on your workspace. * You have generated an ED25519 key pair and configured its public key in GTR. You decrypt the data you receive with the private key. ## Step 2: Connect GTR You do this once, during onboarding. > [!NOTE] > > The Travel Rule legal entity you create here is separate from the legal entity you register in [Address Registry](/reference/legal-entity-setup). Creating one does not create or change the other. 1. **Create a Travel Rule legal entity**, the business subject to the Travel Rule. Most customers need one; if you operate across jurisdictions with different requirements, create one per jurisdiction and assign vault accounts to each. Leave exactly one entity without a vault restriction: it covers every vault account not assigned elsewhere. | Field | Description | | - | - | | `shortName` | Required. | | `fullLegalName` | Full registered name. | | `countryOfRegistration` | ISO 3166-1 alpha-2 code, for example `US`. | | `nationalIdentification` | National identifier, for example an EIN. | | `dateOfIncorporation` | ISO date, for example `2020-01-15`. | | `geographicAddress` | Registered address. | | `vaults` | Vault account IDs this entity covers. | | `discoverable` | `anonymous` (default): counterparties cannot see your details, but Fireblocks confirms the address exists. `hidden`: nothing is disclosed. `discoverable`: full details are shared. | 2. **Create the integration** with the legal entity's `customerId` and GTR's `partnerIdent` from the partner list. The response returns a `customerIntegrationId`, used in every call after this. 3. **Connect the integration** with your GTR API key and secret. Fireblocks stores the credentials and returns them censored in later responses. 4. **Test the connection.** `success: true` confirms the integration can reach GTR. To update your GTR credentials later, submit a ticket to Fireblocks Support. ## Step 3: Set up the policy GTR uses three policy stages: Trigger (Screening Policy), Missing Travel Rule Message, and Outcome (Post-Screening Policy); see [Compliance Policies](/docs/compliance-policies). Missing Travel Rule Message rules can depend on how long a transaction has waited, for example accepting small transactions after 30 minutes but rejecting large ones. Rules are not self-service: fill out a policy template, run it through your compliance approval process, and submit it to Fireblocks Support, who apply it. Read the current policy through the API. Download each policy template and its instructions as CSV files: * Screening Policy: [template (CSV)](/files/policy-templates/screening-policy-template.csv) and [instructions (CSV)](/files/policy-templates/screening-policy-instructions.csv) * Missing Travel Rule Message Policy: [template (CSV)](/files/policy-templates/missing-travel-rule-message-policy-template.csv) and [instructions (CSV)](/files/policy-templates/missing-travel-rule-message-policy-instructions.csv) * Post-Screening Policy: [template (CSV)](/files/policy-templates/post-screening-policy-template.csv) and [instructions (CSV)](/files/policy-templates/post-screening-policy-instructions.csv) Fireblocks enforces limits you cannot configure, so a transaction never waits indefinitely: 1.5 hours for outgoing transactions, 7 days for incoming. Near the limit, a Wait rule is skipped instead of holding the transaction. A transaction's screening status is Completed, Pending, Bypassed (approved without review by your Trigger), or Failed. In a multi-destination transaction, each destination is evaluated separately and the most restrictive result applies to the whole transaction. ### Policy examples Each stage is a list of rules. Fireblocks evaluates the rules from top to bottom, and the first match wins. A rule can be scoped by direction, asset, amount, customer, provider, and source or destination type. Rule shape for each stage: ```ts theme={"system"} // Trigger (Screening Policy): which transactions require screening interface PreScreeningRule { // Matching criteria customerId?: string; direction?: "inbound" | "outbound"; asset?: string; amount?: { range?: { min?: number; max?: number; }; currency: "USD" | "NATIVE"; }; sourceType?: string; destType?: string; // Required action action: "SCREEN" | "PASS"; } // Missing Travel Rule Message: what to do while no message exists interface MissingTRMRule { // Matching criteria: same as the Trigger, plus timing constraints validBefore?: number; // seconds validAfter?: number; // seconds // Required action action: "WAIT" | "ACCEPT" | "REJECT"; } // Outcome (Post-Screening Policy): how to respond to the screening result interface PostScreeningRule { // Matching criteria: same as the Trigger, plus the message status trmStatus?: "ACCEPTED" | "REJECTED" | "PENDING" | "FAILED"; // Timing constraints validBefore?: number; validAfter?: number; // Required action action: "ACCEPT" | "REJECT" | "ALERT" | "WAIT"; } ``` While a destination waits, two separate policies decide what happens: | Situation | Policy | Allowed actions | | - | - | - | | The message was screened and the result is still `PENDING` | Outcome (Post-Screening Policy) | `ACCEPT`, `REJECT`, `WAIT`, `ALERT` | | No Travel Rule Message exists yet | Missing Travel Rule Message | `WAIT`, `ACCEPT`, `REJECT` | The default is `WAIT`, then `ACCEPT`: hold for a window, then accept if nothing changes. Set the hold window with `validAfter` and `validBefore`, in seconds from the start of the screening step. ```ts theme={"system"} // Wait, then accept (default behavior) const waitThenAccept = [ { action: "WAIT", validBefore: 3600 }, // hold up to 1 hour { action: "ACCEPT" }, // then accept ]; // Accept immediately (no hold) const acceptImmediately = [{ action: "ACCEPT" }]; // Block on fallthrough instead of accepting const waitThenReject = [ { action: "WAIT", validBefore: 3600 }, { action: "REJECT" }, ]; ``` To record Travel Rule data without ever holding or blocking a transaction, set both policies to a single `ACCEPT` rule, with no `WAIT`: ```ts theme={"system"} const postScreening = [{ action: "ACCEPT" }]; // PENDING: accept const missingTrm = [{ action: "ACCEPT" }]; // no message: accept ``` ## Step 4: Encrypt the PII GTR rejects plain-text PII. | | Value | | - | - | | Algorithm | Curve25519 | | Key format | ED25519 key pair, converted to Curve25519 | | Your public key | Configured in the GTR Dashboard | | GTR's public key | From your GTR integration settings; the Sumsub public key endpoint does not apply to GTR | Use GTR's open-source encryption tool rather than implementing Curve25519 yourself. It converts the keys, encrypts outgoing IVMS101 data, and decrypts incoming data, independently of the Fireblocks SDK. See [GTR's Fireblocks integration guide](https://www.globaltravelrule.com/documentation/connect-fireblocks-with-gtr). The encrypted result goes in the TRM's `ivms101` object. `filledFields` lists, in dot notation, every IVMS101 field you populated: ```json theme={"system"} { "version": "IVMS101.2020", "data": "", "filledFields": [ "Originator.originatorPerson[].naturalPerson.name.nameIdentifier[].primaryIdentifier", "Beneficiary.beneficiaryPerson[].naturalPerson.name.nameIdentifier[].primaryIdentifier" ] } ``` ## Step 5: Send an outgoing transaction Create the TRM first, then the transaction. 1. **Assess the requirement**, passing the amount, asset, source, destination, and `beneficiaryVaspId`. `decision` is `REQUIRED`, `NOT_REQUIRED`, or `NEED_MORE_INFO`; when `REQUIRED`, `requiredFields` lists the IVMS101 fields to collect. 2. **Encrypt the PII** (Step 4). 3. **Create the TRM** with the same transaction details and the `ivms101` object. 4. **Create the transaction** with `travelRuleMessageId` set to the TRM's `id`, for example `trm_1234567890abcdef`. If the transaction already exists, link the TRM to it instead. In a multi-destination transaction, link a TRM to each destination, matching by `amount` and `destination`; the endpoint does not accept `destinationScreeningId`. ```ts theme={"system"} const { data: assessment } = await fireblocks.trLink.assessTRLinkTravelRuleRequirement({ customerIntegrationId, tRLinkAssessTravelRuleRequest: { amount: "1500", assetId: "ETH", direction: "OUTBOUND", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xabcd..." } }, beneficiaryVaspId: "vasp-456", }, }); if (assessment.decision === "REQUIRED") { const { data: trm } = await fireblocks.trLink.createTRLinkTrm({ customerIntegrationId, tRLinkCreateTrmRequest: { amount: "1500", assetId: "ETH", direction: "OUTBOUND", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xabcd..." } }, beneficiaryVaspId: "vasp-456", ivms101: ivms, // encrypted IVMS101 object }, }); await fireblocks.transactions.createTransaction({ transactionRequest: { assetId: "ETH", amount: "1500", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xabcd..." } }, travelRuleMessageId: trm.id, }, }); } ``` ## Step 6: Receive an incoming transaction Create the TRM after Fireblocks detects the transaction. 1. **Assess the requirement**, passing the `txId` and the `originatorVaspId`. The transaction details are filled in from the `txId`. 2. **Encrypt the PII** you hold for your beneficiary. 3. **Create the TRM** with the `txId`, `originatorVaspId`, and `ivms101` object. 4. **Link the TRM** to the transaction. ## Resolve pending and missing messages Fireblocks notifies you of both cases in the Compliance webhook category; see [Compliance Events & Webhooks](/docs/compliance-events-webhooks) for the payloads. **A TRM needs more data** (`TRLink.RequiredActions`, once per destination). The TRM is `PENDING`. Read its required actions and resolve them one at a time; each call returns the updated TRM, which leaves `PENDING` once the last action is resolved. | Action type | What to do | | - | - | | `UPLOAD_BENEFICIARY_PII` | Encrypt the beneficiary fields listed in `data.beneficiaryRequiredFields` and submit them. | | `MANUAL_REVIEW` | A compliance review at GTR is needed. | **A transaction has no TRM** (`TRLink.NoTRMessage`). It waits in the Missing Travel Rule Message stage until a TRM arrives or your rule times out. To decide without waiting, for example from your own KYC data, submit a manual decision with `action` `ACCEPT` or `REJECT` and an optional `reason` of up to 500 characters, without PII. * The decision applies to every destination currently in that stage; it returns an error if there are none. * Repeating the same action has no effect; a different action replaces the earlier one. * Once a destination has moved past the stage, its decision cannot be changed. * Manual decisions are recorded with `source: "MANUAL"`. TRM statuses are `PENDING`, `ACCEPTED`, `REJECTED`, and `FAILED`. A canceled TRM cannot be reused; create a new one if the transaction goes ahead. ## Testing and monitoring Test with Testnet assets before real value. In the Console, **Transaction History** shows the policies and rules applied to each transaction, GTR's responses, and the final verdict with an explanation. ## Troubleshooting Transactions not screened: check that the vault accounts are assigned to a legal entity and that the Trigger rules match. Unexpected verdicts: check the rule order and the Valid After and Valid Before windows. Integration errors such as 401 or 404 come from GTR; Fireblocks does not validate credentials when you connect and returns the error as is. Check that the app token and secret were created in the same GTR environment and application as this integration. ## Limitations * **Rules are not self-service;** Fireblocks Support applies them. * **Combining providers:** GTR can run alongside Sumsub and TRUST, but not Notabene. * **Disconnecting** permanently deletes the stored credentials and the integration, and pending TRMs may fail. Deleting a Travel Rule legal entity also deletes its integrations, TRMs, and configuration. Neither can be undone. ## On the Help Center For GTR setup and monitoring in the Console, see [GTR](https://support.fireblocks.io/hc/en-us/articles/30614375972892-GTR). # About Identity & Compliance Source: https://developers.fireblocks.com/docs/identity-and-compliance-overview See what Identity & Compliance covers, what you can do through the API or the Console, and which guide covers each capability. Identity & Compliance lets you verify who you are transacting with, screen transactions and addresses for risk, and apply your compliance policies automatically as transactions move through your workspace. It covers counterparty and transaction compliance, not onboarding or KYC for your own end customers, and it does not replace your compliance program: you remain responsible for reviewing flagged activity and filing any required regulatory reports. Most setup happens in the Console; through the API you read policies and results, receive events, act on screened transactions, and set up the API-only checks. Start with [Why Screen Through Fireblocks](/docs/why-screen-through-fireblocks) for where screening runs in a transaction, and [What You Can Connect](/docs/what-you-can-connect) for which checks and providers you can enable. ## Compliance screening | Capability | Console | API | Guide | | - | - | - | - | | Compliance policies | Build, edit, and delete rules (Sumsub and GTR through Support); outage and delay settings; workspace-wide limits | Read rules; outage and delay settings; workspace-wide limits | [Compliance Policies](/docs/compliance-policies) | | Screened transactions | Read the result; bypass, rescreen, unfreeze | Read the full result; bypass, rescreen, freeze, unfreeze | [Transaction Screening Operations](/docs/transaction-screening-operations) | | Screening events | No | Receive transaction status and Travel Rule events | [Compliance Events & Webhooks](/docs/compliance-events-webhooks) | | Customer Reference ID | No | Tag vault accounts, wallets, and transactions with your own customer identifier | [Customer Reference ID](/docs/customer-reference-id) | | Address Registry Screening | Counterparty groups, policy, activation | Counterparty groups, activation | [About Address Registry Screening](/docs/address-registry-screening) | | AML/KYT | Connect the provider | No | [AML/KYT Overview](/docs/aml-kyt-overview) | | Travel Rule via a provider | Connect Notabene | Integrate the SDK; set up Sumsub, GTR, and TRUST; create and attach Travel Rule messages | [Travel Rule Overview](/docs/travel-rule-overview) | | Bring Your Own Screening | No | Timeouts, activation, verdicts (Trigger through Support) | [About Bring Your Own Screening](/reference/bring-your-own-screening-check-developer-guide) | ## Travel Rule for connected accounts | Capability | Console | API | Guide | | - | - | - | - | | Exchanges | Opt-in through your Customer Success Manager | Yes | [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule) | | On/off-ramps | No | Yes | [On/Off-Ramps](/docs/on-off-ramps-travel-rule) | ## Compliance tooling | Capability | Console | API | Guide | | - | - | - | - | | Address Registry | Look up addresses, configure legal entities, export attestations | Look up addresses, register a legal entity, manage participation | [About Address Registry](/reference/address-registry), [Legal Entity Setup](/reference/legal-entity-setup), [Export a Proof of Ownership Document](/docs/export-proof-of-ownership) | | Fireblocks OFAC sanctions screening | Automatic, nothing to configure | Blocked transactions return `status` `BLOCKED` with `subStatus` `BLOCKED_BY_POLICY` | [Fireblocks OFAC Sanctions](https://support.fireblocks.io/hc/en-us/articles/30614329049756-Fireblocks-Compliance-Policy-OFAC-Sanctions) (Help Center) | The [Cyber and Operational Resilience (COR) Compliance Package](https://support.fireblocks.io/hc/en-us/articles/30614376322716-Cyber-and-Operational-Resilience-COR-Compliance-Package) is documented on the Help Center only. ## On the Help Center To configure these capabilities in the Fireblocks Console, see [About Identity & Compliance](https://support.fireblocks.io/hc/en-us/articles/30614325206556-About-Identity-Compliance). # Index Any Contract Source: https://developers.fireblocks.com/docs/index-any-contract Enable on-chain data indexing for any linked EVM smart contract, then query decoded transfers, balances, supply history, and RBAC roles through the Tokenization API. Fireblocks can index on-chain events for any linked EVM smart contract, then expose decoded transfers, balances, supply history, and role-based access control (RBAC) roles through the Tokenization API. ## On-chain data Every time a smart contract does something, it writes a record of it to the blockchain. By their activities, contracts create events, which are stored on-chain as logs attached to the transaction that triggered them. An ERC-20 contract, for example, creates a `Transfer` event on every mint, burn, and transfer, and an `Approval` event whenever a holder authorizes a spender. Contracts that implement RBAC create events such as `RoleGranted` and `RoleRevoked`. You can receive these events through [Tokenization on-chain data webhooks](/reference/webhooks-structures-eventtypes-tokenization). Logs are the contract's own record of what happened and when. Reading a contract's current state (calling `balanceOf` for an address, or `totalSupply`) tells you where things stand right now. Logs tell you how they got there: who held the token last month, when supply changed, which address granted a role, and in which block. The raw logs are not directly usable. Each one is a set of hashed topics and packed hexadecimal data, and getting a full history means requesting logs block by block from a node, then decoding them against the contract's Application Binary Interface (ABI). Answering a question like "what was this address's balance on June 30th" means replaying every transfer that touched it since the contract was deployed. Indexing does that work for you. Fireblocks reads the contract's logs from the chain, decodes them using the contract ABI, and stores the result so you can query it directly: transfers, balances at a point in time, supply history, and active roles. ## Coverage Fireblocks supports on-chain data indexing for any [linked](/api-reference/tokenization/link-a-contract) EVM smart contract on a [supported network](#supported-networks), not only Fireblocks reference smart contracts (ERC20F, Access Registry). Once indexing is enabled for a contract, you can query decoded transfer events, balances, balance history, total supply, and active RBAC roles through the Tokenization API. ## How it works Indexing is enabled per contract from the Fireblocks Console, then read through the [API endpoints](#api-surface) below. Enabling indexing for a contract: * Backfills historical on-chain events for the contract (a *scan*), so historical queries return complete data instead of only events from the moment you enabled indexing. * Starts ongoing tracking of new events: transfers, supply changes, freezes, role grants, ownership transfers, and cross-chain bridge events (for example, LayerZero Omnichain Fungible Token (OFT) events). > [!NOTE] > > **Audit trail:** Indexing also captures contract upgrades, authorization (RBAC) changes, and cross-chain bridging as part of the same event feed. This gives you an audit trail for on-chain activity that did not originate from Fireblocks, which is useful when reconciling bring-your-own (BYO) token contracts against your own records. ## Enable indexing for a contract Indexing is a per-contract setting, managed from the Console: **1. Open the contract** Open the token or the contract's details page in the Console. **2. Open On-chain data** Select **On-chain data** from the contract header. **3. Turn on indexing** Turn on **Event indexing**. Optionally, turn on **Webhook notifications** to include the contract in your workspace's Tokenization webhook notifications. **4. Scan history** Select **Scan** under **Scan history** to backfill historical events. This can take from a few minutes to several days, depending on the contract's activity volume. > [!NOTE] > > If a scan stops before it concludes, the same modal shows how far it got and lets you restart it. Poll the endpoints below, or use the workspace's [Tokenization webhook](/reference/webhooks-structures-eventtypes-tokenization), to determine when a contract has concluded its initial scan and has data available. ## API surface The following endpoints are available once indexing is enabled for a token contract. `{id}` is the Fireblocks token link ID. ### Transfers ```http theme={"system"} GET /v1/tokenization/tokens/{id}/transfers ``` Returns paginated ERC-20 transfer events for the token contract. Supports filtering by date range, sender address, and receiver address. See [Get onchain transfers for a token](/api-reference/tokenization/get-onchain-transfers-for-a-token). Response type: `OnchainTransfersPagedResponse`. ### Transactions and events ```http theme={"system"} GET /v1/tokenization/tokens/{id}/transactions ``` Returns paginated on-chain transactions for the token contract, filterable by date range. This is the decoded event feed shown in the Console's **Events** tab, including transfers, supply changes, freezes, role grants, ownership transfers, and cross-chain bridge events. See [Get onchain transactions for a token](/api-reference/tokenization/get-onchain-transactions-for-a-token). Response type: `OnchainTransactionsPagedResponse`. ### Balances (all holders) ```http theme={"system"} GET /v1/tokenization/tokens/{id}/balances ``` Returns the latest balance for each unique address holding the token. See [Get latest balances for all holders of a token](/api-reference/tokenization/get-latest-balances-for-all-holders-of-a-token). Response type: `AddressBalancePagedResponse`. ### Balance (single account) ```http theme={"system"} GET /v1/tokenization/tokens/{id}/balances/{accountAddress} ``` Returns the latest balance for a specific account address. See [Get the latest balance for a specific account](/api-reference/tokenization/get-the-latest-balance-for-a-specific-account). Response type: `AddressBalanceItemDto`. ### Balance history ```http theme={"system"} GET /v1/tokenization/tokens/{id}/balances/{accountAddress}/history ``` Returns paginated balance history for an account. Supports time-range filtering and interval binning. See [Get balance history for a specific account](/api-reference/tokenization/get-balance-history-for-a-specific-account). Response type: `BalanceHistoryPagedResponse`. ### Total supply ```http theme={"system"} GET /v1/tokenization/tokens/{id}/total_supply ``` Returns paginated total supply history. Supports time-range filtering and interval binning. See [Get historical total supply for a token](/api-reference/tokenization/get-historical-total-supply-for-a-token). Response type: `TotalSupplyPagedResponse`. ### Roles (RBAC) ```http theme={"system"} GET /v1/tokenization/tokens/{id}/rbac ``` Returns the list of currently active roles for the token contract. See [Get active RBAC roles for a token](/api-reference/tokenization/get-active-rbac-roles-for-a-token). Response type: `ActiveRolesResponse`. ## Supported networks | Chain | Mainnet | Testnet | | - | - | - | | Arbitrum | `arbitrum-mainnet` | `arbitrum-sepolia` | | Avalanche | `avalanche-mainnet` | `avalanche-fuji` | | Base | `base-mainnet` | `base-sepolia` | | BNB Chain | `bnbchain-mainnet` | `bnbchain-testnet` | | Ethereum | `ethereum-mainnet` | `ethereum-sepolia`, `ethereum-hoodi` | | Gnosis | `gnosis-mainnet` | — | | Optimism | `optimism-mainnet` | `optimism-sepolia` | | Polygon | `polygon-mainnet` | `polygon-amoy` | | Monad | `monad-mainnet` | `monad-testnet` | | Arc | — | `arc-testnet` | ## Scope On-chain indexing covers any linked EVM smart contract on the networks above, including BYO token contracts, which make up roughly 40% of contracts in the Tokenization Engine. This also unlocks the token management experience for any ERC-20 contract, along with upcoming features such as Yield Payer. # Integrate Fireblocks with Third-Party Service Providers Source: https://developers.fireblocks.com/docs/integrate-fireblocks-with-third-party-service-providers Many organizations utilizing Fireblocks seek to integrate their workspace with third-party service providers such as accounting solutions, portfolio aggregation tools, trading platforms, and more. These providers can offer valuable insights, operational efficiencies, and additional functionality by integrating directly with a Fireblocks environment. However, since Fireblocks might not have native integrations with all these services, it is crucial to follow best practices to ensure that assets and data remain secure while maximizing the integration's effectiveness. This guide is structured into two main sections: * **For Fireblocks Clients**: Provides step-by-step recommendations for securely integrating third-party service providers with your Fireblocks workspace, focusing on access control, API key management, and other security practices. * **For Third-Party Service Providers**: Offers guidance on how to securely connect with a client’s Fireblocks environment, emphasizing API key management, efficient data synchronization, and ensuring the client’s security requirements are met. By addressing the specific concerns of both clients and providers, this guide aims to facilitate a smooth and secure integration process that benefits both parties. # Fireblocks Clients ## Best Practices for Integration ### 1. Limit Access Levels Ensure third-party providers are granted **Viewer** or **Editor** roles only—never **Signer** or **Admin** access. This principle applies to both API-based and UI-based integrations. Providing limited access minimizes risk exposure to your Fireblocks environment. ### 2. Secure API Key Management If a third-party provider requires API access, the provider should handle key generation securely: * The provider should create their own RSA private key and a Certificate Signing Request (CSR). Only the CSR should be shared with you. * **Never accept private keys over the internet**. Refer to the [Fireblocks API Key Management Documentation](/docs/manage-api-keys) for details on secure key management. ### 3. Use Dedicated API Keys Always generate a new API key specific to the third-party provider. Avoid reusing API keys from internal operations to mitigate risks and facilitate easier access management. ### 4. Whitelist Provider IP Addresses To enhance security, whitelist the provider’s server IP addresses for the specific API key. This additional security measure helps ensure only authorized servers have access. More information can be found in the [IP Whitelisting Documentation](/docs/whitelist-ips-for-api-keys). ### 5. Initial Data Synchronization For the first data sync, it's best to manually share balance and address reports from the Fireblocks Console to avoid overloading the API and hitting rate limits. ### 6. Enable Webhook Notifications To keep data synchronized without excessive API polling, enable webhooks to push real-time updates from Fireblocks to the provider. Visit the [Webhook Configuration Guide](/reference/configure-webhook-urls) for setup instructions. ### 7. Signing Transactions If transaction signing is required, do not grant signing privileges directly to third-party providers. Use the **Designated Signer** feature and define a **Policy**. This approach maintains security by requiring your approval before any transaction is signed by the provider. # Third-Party Service Providers (Partners) ## Best Practices for Integration ### 1. Understand Access Roles When integrating with a Fireblocks client, expect to receive **Viewer** or **Editor** access only. **Signer** and **Admin** roles are not granted to maintain the client's security. These roles should be sufficient for most integrations to view data and perform necessary actions. Learn more about Fireblocks user roles in the ["Manage Users](/docs/manage-users)" guide. ### 2. Secure API Key Management As a provider, you are responsible for securely generating and managing API keys: * Generate an RSA private key and CSR, sharing only the CSR with the Fireblocks client. * **Never share private keys with clients or transmit them over the internet**. Consult the [Fireblocks API Key Management Documentation](/docs/manage-api-keys) for specific details on managing API keys securely. > [!WARNING] > > Always generate the RSA private key yourself and share the Certificate Signing Request (CSR) with the client. Never request the client to provide their private key! ### 3. Request a Dedicated API Key For every new client integration, request a separate API key. This practice allows clear separation of access per client and aids in efficient management and troubleshooting. ### 4. Provide IP Addresses for Whitelisting To ensure your integration is secure, supply the client with your server's IP addresses for whitelisting. This step helps secure API communication and restricts access to trusted IPs. ### 5. Efficient Data Synchronization To avoid overloading the client's Fireblocks API and ensure a smooth initial data sync, it's recommended to start with balance and address reports shared directly from the Fireblocks Console. Coordinate with the client for a safe and efficient data transfer process. ### 6. Use Webhook Notifications Reduce the need for frequent API polling by configuring webhooks that allow real-time updates from the Fireblocks client. This setup improves efficiency and ensures up-to-date data synchronization. Check the [Webhook Configuration Guide](/reference/configure-webhook-urls) for configuration steps. ### 7. Transaction Initiation Without Signing If your integration requires initiating transactions, ask the client to configure your access according to their **Policies**. This will ensure that your API key can initiate transactions, but any signing or approval must be performed by the designated signer configured in the Policy. This designated signer should be the customer themselves! # Interact With Smart Contracts Source: https://developers.fireblocks.com/docs/interact-with-smart-contracts ## Interaction with Smart Contracts Interacting with a smart contract involves calling read or write methods on a specific contract. These functions can range from simple standard ERC-20 methods to complex smart contract logic invocations. Fireblocks offers several ways to interact with smart contracts: 1. **Interact with a Deployed Smart Contract via the Fireblocks Console** - Learn more [here](https://support.fireblocks.io/hc/en-us/articles/12265198149660-Managing-smart-contracts-for-tokenization). 2. **Use the Fireblocks EVM Web3 Provider or Local JSON RPC Tools**. 3. **Use the Fireblocks REST API**. ## Using the Web3 Provider and Local JSON RPC Tools These tools allow you to seamlessly integrate Fireblocks into your codebase. ### EVM Web3 Provider The EVM Web3 Provider is an EIP1193 compatible provider that can be integrated into your EVM client library (such as ethers, web3, viem) with a simple configuration. For more details, see the [Web3 Provider developer guide](/reference/evm-web3-provider). ### Local JSON RPC Local JSON RPC provides the same functionality as the Web3 provider but is designed for customers who do not use JavaScript-based tools for their smart contract development and integration. For more details, see the [Local JSON RPC developer guide](/reference/evm-local-json-rpc). ## Using the Fireblocks REST API Direct interaction with smart contracts via the Fireblocks API can be achieved by calling the Create Transaction API endpoint. To perform a contract call, set the `operation` parameter to `CONTRACT_CALL` and include the `contractCallData` parameter in the `extraParameters` object of the POST request body. The `contractCallData` value should be the hex-encoded data you want to pass to the contract. This can be structured using well-known libraries such as web3.js, ethers.js, or similar. For more details, see the [Contract calls developer guide](/transactions/contract-calls). ### Interpret contract call results with networkRecords A single `CONTRACT_CALL` transaction can move several assets on-chain (for example, a DeFi swap or withdrawal that returns multiple tokens). The top-level `assetId`, `amount`, and `netAmount` fields on the transaction response describe only the outer call, and are commonly `0` when no native asset was sent. To see the actual asset movements produced by the contract, read the `networkRecords` array. Each [NetworkRecord](/reference/transaction-objects#networkrecord) entry describes one intermediate blockchain transfer that Fireblocks aggregated under the parent transaction, with fields including `assetId`, `netAmount`, `source`, `destination`, `destinationAddress`, `txHash`, and `status`. To classify movements from the perspective of one of your vault accounts: * **Sent asset:** entries where `source.id` equals your vault account ID. * **Received asset:** entries where `destination.id` equals your vault account ID. * **Skip zero-value noise:** entries where `netAmount` is `"0"`. > [!NOTE] > > `networkRecords` is populated after the transaction is broadcast and Fireblocks has parsed the receipt, so it may be empty on early transaction statuses. Wait for a terminal status (for example, `COMPLETED`) before relying on it. For simple `TRANSFER` operations, the top-level `assetId` and `amount` are sufficient and `networkRecords` is empty. # Approve Amount Cap In the ERC20 standard, the `approve` operation is a crucial function that allows a token owner to authorize another address (usually a smart contract) to spend a specified amount of tokens on their behalf. This function is commonly used in DeFi applications, token swaps, and other scenarios where tokens need to be managed by smart contracts. ### How It Works When you call the `approve` function, you specify two parameters: * **spender**: The address that will be allowed to spend the tokens. * **amount**: The maximum number of tokens that the `spender` is allowed to spend. The `approve` function updates the allowance, which is the amount of tokens that the `spender` can use. This is recorded in the token contract’s internal mapping. ### Example Use Case Suppose you want to use a DeFi platform to lend your tokens. You would first call the `approve` function to allow the platform’s smart contract to transfer tokens from your address. Once approved, the platform can transfer the specified amount of tokens from your address to the lending pool. The **Amount Cap** limits the amount smart contracts and third-party dApps may withdraw on your behalf. This reduces the risk associated with granting an unlimited approval amount. Transaction amounts are then automatically changed from the maximum amount to the user-specified limit for all Approve transactions created with dApps. Learn more about limiting the [Amount Cap for Approve transactions](https://support.fireblocks.io/hc/en-us/articles/4404616097426-Amount-Cap-for-Approve-transactions) via the Fireblocks Console. # TRUST Source: https://developers.fireblocks.com/docs/interact-with-trust Prove you control a vault account address on the TRUST network: create a proof of address, retrieve the signature, and submit it to TRUST. TRUST (Travel Rule Universal Solution Technology) is a Travel Rule network. Before TRUST accepts Travel Rule data for an address, you prove that you control it; Fireblocks signs that proof with Typed Message Signing, which shows exactly what is being signed instead of an opaque payload. TRUST is free, available to every workspace, and works alongside any other Travel Rule provider. It is set up through the API only and has no screening policy. ## What you can do through the API | Step | Task | Console | API | | - | - | - | - | | 1 | Set up the Fireblocks SDK in your system | No | No API call; install the TypeScript SDK | | 2 | Get a UUID from TRUST (`CreateAddressOwnership`) | No | No Fireblocks API call; request it in TRUST | | 3 | List the addresses of an asset wallet, to prove a derived `BTC` or `BTC_TEST` address | No | [Get addresses (Paginated)](/api-reference/vaults/get-addresses-paginated) | | 3 | Create a proof of address | No | [`POST /screening/travel_rule/providers/trust/proof_of_address`](/api-reference/travel-rule/create-trust-network-proof-of-address) | | 4 | Get the encoded signature to submit to TRUST | No | [`GET /screening/travel_rule/providers/trust/proof_of_address/{transactionId}`](/api-reference/travel-rule/retrieve-trust-network-proof-of-address-signature) | | 4 | Submit the signature to TRUST | No | No Fireblocks API call; submit it in TRUST | ## Step 1: Set up the Fireblocks SDK Install the Fireblocks SDK in your system; proofs are created and retrieved from your code. See [TypeScript SDK](/reference/typescript-sdk). ## Step 2: Get a UUID from TRUST Request address ownership in TRUST (`CreateAddressOwnership`). TRUST returns a UUID that ties the proof to your request. ## Step 3: Create the proof of address | Field | Type | Description | | - | - | - | | `vaultAccountId` | string | Required. The vault account that holds the address. | | `asset` | string | Required. The Fireblocks asset ID whose address you are proving, for example `ETH`; see [Supported blockchains](#supported-blockchains). | | `prefix` | string | Required. Your TRUST prefix, used when signing the message. 1–255 characters. | | `trustUuid` | string (UUID) | Required. The UUID from Step 2. | | `address` | string | Optional. Maximum 255 characters. A derived address of the vault account to prove: a deposit address other than the default one. UTXO-based assets only: `BTC` and `BTC_TEST`. It must exist in the vault account for this asset, exactly as listed. If you leave it out, Fireblocks proves the default address. See [Prove a derived address](#prove-a-derived-address-utxo-based-assets). | Accepts an optional `Idempotency-Key` header, valid for 24 hours. The request returns `400` if you send `address` for an asset other than `BTC` or `BTC_TEST`, or if the address does not belong to the vault account for this asset. ```ts theme={"system"} const { data } = await fireblocks.travelRule.createTrustProofOfAddress({ trustProofOfAddressRequest: { vaultAccountId: "0", asset: "ETH", prefix: "trust_verification", trustUuid: "550e8400-e29b-41d4-a716-446655440000", }, }); console.log(data.transactionId, data.status); // e.g. PENDING_SIGNATURE ``` The proof is a typed message signing transaction. It follows the standard [transaction statuses](/reference/primary-transaction-statuses) and your workspace's Policies for approval and signing. ### Prove a derived address (UTXO-based assets) By default, Fireblocks proves the default address of the asset wallet. For UTXO-based assets (`BTC` and `BTC_TEST`), an asset wallet can have several deposit addresses. To prove one of the others, a derived address, pass it as `address`. 1. List the addresses of the asset wallet with [Get addresses (Paginated)](/api-reference/vaults/get-addresses-paginated). Copy the address you want to prove, exactly as listed. 2. Create the proof with that address: ```ts theme={"system"} const { data } = await fireblocks.travelRule.createTrustProofOfAddress({ trustProofOfAddressRequest: { vaultAccountId: "0", asset: "BTC", prefix: "trust_verification", trustUuid: "550e8400-e29b-41d4-a716-446655440000", address: "
", }, }); ``` Fireblocks signs the message `prefix` + `address` + `trustUuid`, with no separators, using the key of that address. Both the SegWit and the Legacy form of an address are accepted. You cannot choose your own derivation path: only addresses that already exist in the vault account can be proven. ## Step 4: Retrieve the signature and submit it to TRUST Poll the GET endpoint with the `transactionId` from Step 3 until the transaction is signed. It always returns `200` with `transactionId` and `status`; `signature` and `publicKey` are included only once the transaction is signed. | `status` | Response includes | What to do | | - | - | - | | `QUEUED`, `PENDING_AUTHORIZATION`, `PENDING_SIGNATURE` | `transactionId`, `status` | Keep polling. | | `SIGNED`, `COMPLETED` | `transactionId`, `status`, `signature`, `publicKey` | Send the signature to TRUST. | | `REJECTED`, `CANCELLED`, `FAILED` | `transactionId`, `status` | Create a new proof of address. | TRUST requires the `publicKey` for SUI, TON, and XTZ. For `BTC` and `BTC_TEST`, the signature is Base64 text of `v`, `r`, and `s` (65 bytes), where `v` is the recovery value plus 31. The proof is complete when TRUST responds `VALID`. | Error | Meaning | | - | - | | `400` | `transactionId` is not a valid UUID v4. | | `404` | No transaction exists with this `transactionId`. | | `503` | The transaction lookup is temporarily unavailable. Try again. | | `500` | The status is `SIGNED` or `COMPLETED`, but no signed message exists. | ## Supported blockchains > **Blockchains supported for TRUST proof of address** > > | Chain | TRUST ID | Fireblocks asset ID | > | - | - | - | > | Bitcoin | `BITCOIN` | `BTC` | > | Ethereum | `ETHEREUM` | `ETH` | > | Solana | `SOLANA` | `SOL` | > | Tron | `TRON` | `TRX` | > | Stellar | `STELLAR` | `XLM` | > | Near | `NEAR` | `NEAR` | > | Cosmos | `COSMOS` | `ATOM_COS` | > | Algorand | `ALGORAND` | `ALGO` | > | Tezos | `TEZOS` | `XTZ` | > | TON | `TON` | `TON` | > | Ethereum Classic | `ETHEREUMCLASSIC` | `ETC` | > | Sui | `SUI` | `SUI` | > | Avalanche C-Chain | `AVALANCHE_C_CHAIN` | `AVAX` | > | Arbitrum | `ARBITRUM` | `ETH-AETH` | > | Base | `BASE` | `BASECHAIN_ETH` | > | BSC | `BSC` | `BNB_BSC` | > | Celo | `CELO` | `CELO` | > | Cronos | `CRONOS` | `CRONOS` | > | Hyperliquid (HyperEVM) | `HYPERLIQUID` | `HYPE_ETH` | > | Optimism | `OPTIMISM` | `ETH-OPT` | > | Polygon PoS | `POLYGON_POS` | `MATIC_POLYGON` | > | Sei | `SEI` | `SEI` | > > Testnet variants of these chains are also supported, for example `ETH_TEST5` and `BTC_TEST`. ## Limitations * Proof of address uses the default address of the vault account. For `BTC` and `BTC_TEST`, you can prove a derived address of the vault account with `address`. For other assets, other deposit addresses cannot be verified. * Only addresses that already exist in the vault account can be proven. You cannot set a custom derivation path. * TRUST has no screening policy; it covers proof of address only. * TRUST is an external network, not operated by Fireblocks. ## On the Help Center For how TRUST proof of address works, see [TRUST](https://support.fireblocks.io/hc/en-us/articles/30614328693788-TRUST). # Fireblocks Developer Docs Source: https://developers.fireblocks.com/docs/introduction Welcome to the Fireblocks Developer Portal. Whether you're building a crypto exchange, a DeFi application, or an embedded wallet experience, you'll find everything you need to integrate Fireblocks into your product. > **[Getting Started](/docs/quickstart)** > > Connect your AI agent, set up your API key, and make your first call with the SDK or CLI. > **[API Reference](/reference/api-overview)** > > Explore all Fireblocks REST API endpoints with interactive examples and code snippets. > **[What Is Fireblocks?](/docs/what-is-fireblocks)** > > Understand the platform architecture, security model, and core concepts. > **[SDKs & Dev Tools](/reference/typescript-sdk)** > > Official SDKs for TypeScript, Python, and Java, Fireblocks CLI plus Postman collections and dev tooling. ## Explore by Use Case Choose the integration pattern that matches what you're building. > **[Wallet as a Service](/docs/wallet-as-a-service)** > > Embed non-custodial wallets into your application with Fireblocks MPC technology. > **[Treasury Management](/docs/treasury-management)** > > Manage institutional digital asset operations across exchanges, DeFi, and staking. > **[Tokenization](/docs/tokenization)** > > Issue, manage, and distribute digital tokens using smart contract templates. > **[Self-Custody Infrastructure](/docs/self-custody-infrastructure)** > > Build self-custody solutions with enterprise-grade MPC key management. ## Platform Overview Fireblocks provides a unified platform for building on blockchain with enterprise-grade security. ## Key Capabilities > **[Vault Accounts](/docs/overview)** > > Create and manage direct custody wallets for secure asset storage. > **[Transactions](/transactions/create-transactions)** > > Transfer, swap, and manage digital assets across blockchains. > **[Webhooks](/reference/webhooks-overview)** > > React to real-time events like deposits, withdrawals, and status changes. > **[Smart Contracts](/docs/interact-with-smart-contracts)** > > Deploy and interact with smart contracts across EVM and non-EVM chains. > **[Staking](/docs/stake-assets)** > > Stake assets and manage validator operations programmatically. > **[Compliance](/docs/identity-and-compliance-overview)** > > Integrate AML screening and travel rule compliance into your workflows. ## Start Building **1. Connect your AI agent** Install the [Fireblocks Documentation MCP](/docs/quickstart) first so Cursor, Claude Code, Codex, or any other agent can read these docs while you build — and so your implementation stays grounded in the canonical documentation even when you're not using an agent. **2. Get a Sandbox Account** [Sign up for a free Fireblocks sandbox](https://www.fireblocks.com/developer-sandbox-sign-up) to get API access and test credentials. **3. Set Up Authentication** [Generate a CSR and API key](/docs/manage-api-keys) to authenticate your API requests using JWT signing. **4. Choose your track** Install an [SDK](/reference/typescript-sdk) for application code, or the [Fireblocks CLI](/docs/fireblocks-cli) for fast workspace operations and agent workflows. [Getting Started](/docs/quickstart) covers both. **5. Follow a Guide** Walk through our guides to [create wallets](/reference/create-vault-account), [send transactions](/transactions/create-transactions), or [set up webhooks](/reference/webhooks-overview). # Tokenize Assets Source: https://developers.fireblocks.com/docs/issue-new-tokens Tokenization is the representation of an asset, which could be a real-world physical asset or a digitally native one, on a distributed ledger. Tokens represent ownership, control, claim, or a right to the underlying asset or service. With Fireblocks, you can issue and manage tokens for your specific use case, such as stablecoins, carbon credits, in-game assets, concert tickets, real estate, memorabilia, artwork, NFTs, or any other asset you want to tokenize. # Tokenization in Fireblocks Tokenization is a premium feature that requires an additional purchase. If you don’t have access to this feature, contact your Customer Success Manager to discuss having it enabled in your Fireblocks workspace. The Tokenization feature allows you to deploy and manage tokenized assets and their lifecycle via the Fireblocks Console or the Fireblocks API. ## Supported assets and blockchains Fireblocks supports token issuance and management for the following networks and assets: * All EVM-compatible blockchains supported by Fireblocks * Stellar * Ripple ## Token lifecycle management Generally, a token's lifecycle involves: 1. Issuing new tokens or linking previously issued tokens. 2. Viewing token information on the Tokenization page. 3. Executing operations (minting, distributing, burning, etc.). You can manage the tokens in your workspace using: ### The Fireblocks Console For more details, see [Tokenization in the Fireblocks Console](https://support.fireblocks.io/hc/en-us/articles/8760035076892-Tokenization-Page). * EVM blockchains: You can call any smart contract function, such as minting and burning, for your EVM-based tokens on the Tokenization page. * Stellar and Ripple: You can issue, mint, burn, and transfer tokens on the Tokenization page. You can also create wallets with automatically established trustlines (an explicit opt-in to hold the token). * All supported assets: You can link tokens on the Tokenization page, where you can view detailed information about each of them. ### The Fireblocks API * EVM blockchains: You can mint, burn, and transfer tokens using specific API operations. For all other operations, you can use contract calls. * Stellar and Ripple: You can issue, mint, burn, and transfer tokens. You can also create wallets with automatically established trustlines. > [!NOTE] > > ### Explore the [Tokenization APIs](/reference/issuenewtoken) in the Fireblocks API Reference # Overview Source: https://developers.fireblocks.com/docs/list-supported-assets-1 Fireblocks supports thousands of assets across dozens of blockchain networks and frequently adds support for new assets. All supported assets share the same name and asset ID across every customer workspace where they are available. To see all assets supported in your workspace, use the Fireblocks API as described in [List assets](/reference/listassets). ## Globally supported assets Globally supported assets are available by default when you create new asset wallets in any workspace. Fireblocks publishes a complete [list of globally supported assets](https://support.fireblocks.io/hc/en-us/articles/6641409557532-Globally-supported-assets), which Fireblocks refreshes monthly. ## Locally supported assets Locally supported assets only appear in workspaces that have manually added or requested them. They are available to transfer to any destination that supports them. Before transferring a local asset to a Fireblocks Network destination, confirm that the counterparty supports it. For more information, see the following Help Center articles: * [Adding EVM assets to a workspace](https://support.fireblocks.io/hc/en-us/articles/5601259928220-Adding-EVM-assets-to-a-workspace) * [Adding non-EVM assets to a workspace](https://support.fireblocks.io/hc/en-us/articles/6641659764508-Adding-non-EVM-assets-to-a-workspace) # Manage API Access Source: https://developers.fireblocks.com/docs/manage-api-keys # API Authentication The Fireblocks API uses an API key and a request-signing process to provide a highly secure communication protocol. You can create API keys through the Fireblocks Console or the API. ## Create an API key in the Console To create your first API key: 1. Generate an RSA 4096 private key and CSR file: ```bash Shell theme={"system"} openssl req -new -newkey rsa:4096 -nodes -keyout fireblocks_secret.key -out fireblocks.csr -subj '/O=' ``` 2. In the Fireblocks Console, go to the [Developer center's API users page](https://console.fireblocks.io/v2/developer/api-users). 3. Select **Add API user**. 4. Enter a display name for the API key (up to 30 characters) and select the appropriate role. Permissions follow the same [user roles](/docs/manage-users) used for Console users. 5. Upload the CSR file you generated in step 1. 6. Select whether a Co-signer setup is required. 1. If you're in a testnet workspace, you can connect to the [Fireblocks Communal Test Co-signer](/cosigners/overview/use-communal-cosigner). 2. Only select **First user on this machine** if you're using the API user to install a new SGX Co-signer. 7. Select **Add user**. After the required approval quorum is reached, the API key appears in the API users list. You can copy the API key from there by hovering your pointer over the value in the **API User (ID)** column. Every request using this API key must follow the [Fireblocks API authentication scheme](/reference/signing-a-request-jwt-structure). ## Create an API key via the Fireblocks API Complete step 1 from the Console procedure above to generate your RSA key and CSR file, then call the [Create API Key](/reference/createapiuser) endpoint to complete the API key creation operation. # API key management best practices Your API credentials are a prime target for attackers. Fireblocks recommends following these best practices to work securely with our APIs. ## Role-based access control Provision each API user's role according to the principle of least privilege. Fireblocks supports multiple roles for role-based access control, and an API user can hold any one of them. Create separate API users to enforce duty separation and establish clear security boundaries. > [!TIP] > > **Best practice:** Create at least two API users with distinct roles. For example, a Viewer role for read-only operations and a Signer role with transaction-signing capabilities. ## Policy rules for API users API users with transaction initiation and signing capabilities can execute transactions like any Console user. Create strong Policy rules to govern what transaction types these API users can conduct and from which accounts. > [!TIP] > > **Best practice:** Limit API users to transact from a specific vault account and up to a defined amount. This blocks or flags transactions that exceed the pre-defined limits or originate from unauthorized accounts. ## IP address allowlisting Each API user can restrict API calls to specific IP addresses. If no IP addresses are allowlisted, the API accepts requests from any address. Explicitly allowlist only the IP addresses you expect to call the Fireblocks API. > [!WARNING] > > **Address format:** Only /32 IP addresses are accepted. Do not enter addresses as a range of values. ## Generating and storing RSA 4096 private keys Each API user requires a public/private key pair to sign requests. Keep your `fireblocks_secret.key` file safe and secure at all times. To protect your key pair: * Generate a unique CSR file and key pair for each API user. This limits the blast radius if one user's keys are compromised. * Generate the CSR file and key pair in an offline (air-gapped) environment for added security. * Store `fireblocks_secret.key` in a hardened environment with advanced security controls such as multi-factor authentication and endpoint protection. ## Storing API keys outside your codebase Never embed API keys directly in code or commit them to your source tree. Keys embedded in code can be accidentally exposed, especially if you use a public source control system such as GitHub. Instead, store API keys in environment variables or in files outside your application's source tree. ## Rotating secrets Rotation works differently depending on the API user type: * **Fireblocks API users:** Because authentication relies on a CSR and associated secret key, create a new API user to rotate credentials. * **Co-signers:** Re-enroll the API user to obtain a new pairing token, then complete the pairing process to receive a new refresh token. * **Fireblocks Agent for KeyLink:** Uses the pairing token, access token, and device. This user type does not hold MPC keys. > [!TIP] > > **Best practice:** Before rotating, use the [Get all API keys](/reference/getapikeys) endpoint to list all API keys paired to a specific API Co-Signer. # Manage Deposits at Scale Source: https://developers.fireblocks.com/docs/manage-deposits-at-scale ## Overview One of the most important processes for a business is the ability to handle deposits made by clients. To facilitate this, each client needs their own wallet for the asset they want to deposit. This requirement applies to both scenarios: deposits made by clients from external sources and on-ramp processes such as purchasing crypto and storing it in their address. Both flows result in the creation of a new wallet for the specific user. Businesses serving hundreds of thousands or even millions of users need to create and manage these addresses and the deposited funds in the most operationally and cost-effective ways. In this guide, we will outline the recommended workflows for Fireblocks clients to achieve an efficient setup for processing user deposits. The topics covered include: * Setting up the correct wallet structure for your use case * Receiving notifications on incoming transfers * Processing UTXO and Tag/Memo-based asset deposits * Processing account-based asset deposits * Best practices for generating deposit addresses * Validating wallet balances * Maintaining an internal ledger ## Setup the Wallet Structure The wallet structure, or as we call it in Fireblocks, the vault structure, that you set up will directly impact the capabilities, efficiency, and operational load of your business. Fireblocks recommends one of the following two setups, depending on the type of your operation: * **Segregated Vault Structure**: Ideal for customers running a B2B business. This setup is recommended for those who want full segregation of their clients' funds. * **Omnibus Vault Structure**: Ideal for customers running a B2C business. This setup is recommended for those who want to create a dedicated wallet for each user and then sweep the funds into a single Treasury wallet. For more details, see the Fireblocks [Vault Structure types](/docs/create-direct-custody-wallets#types-of-vault-structures). ## Get Notifications for Incoming Transactions To receive notifications about incoming transactions to your Fireblocks wallets, we recommend using the Webhook service. Webhooks are push notifications sent by Fireblocks to a URL specified by the customer. This service sends notifications about various events in your Workspace, including incoming funds. Each incoming transaction triggers a series of events, starting with an alert that a new transaction has been identified, followed by subsequent notifications about the transaction's completion status. For more details, see the Fireblocks [Webhook service](/reference/configure-webhook-urls). ## Process UTXO and Tag/Memo Based Assets Deposits Customers who find the Omnibus structure suitable for their business needs and work with UTXO-based assets will notice that all such assets (e.g., BTC, BCH, LTC, DOGE, ADA) are deposited into a single "Deposit" vault account. Each user has their own deposit address identified by the `description` parameter. Processing these funds is straightforward in terms of address generation and management, but it introduces some potential challenges that should be considered during the integration phase. When running a retail-facing business and processing a large volume of UTXO-based asset deposits, customers may find that their deposit vault account accumulates a significant number of unspent transaction outputs (UTXOs) for a specific asset. Efficiently managing these UTXOs is crucial for the following reasons: 1. **Creating Outgoing Transactions**: Customers might want to move deposited funds (or part of them) to different venues such as exchange accounts, withdrawal vaults, or even a cold wallet. The number of UTXOs that can be selected for a transaction is limited to 250 UTXOs per transaction. This limitation could prevent customers from moving the desired amount if the selected 250 UTXOs do not add up to the intended amount. This depends on the UTXO selection mechanism in your Fireblocks wallet, which by default chooses the **smallest confirmed UTXOs first**. The selection mechanism can be adjusted to select the largest confirmed UTXOs first, based on customer preference. 2. **Fee Efficiency**: If a wallet has many small UTXOs, creating outgoing transactions will require selecting more UTXOs to reach the required amount. This results in larger transaction sizes and higher fees, making the transaction more attractive to miners/validators. To overcome the mentioned in the above, customer should implement a UTXO consolidation logic as described in the following section. ### Unique deposit addresses via shared wallets For UTXO-based chains, we recommend a similar approach to memo-based chains. Calling the [Create a new deposit address](/reference/createvaultaccountassetaddress) endpoint generates a new deposit address under the same wallet. Each end user receives a unique deposit address while funds are still received into a shared wallet. End-user deposits are identified by the receiving address included in the transaction notification, eliminating the need to create separate wallets per user. ### UTXO Consolidation UTXO-based transactions in Fireblocks have a limit of 250 inputs (UTXOs) per transaction. Additionally, there must be a logic to select the inputs for a transaction, which Fireblocks implements as follows: 1. **DEFAULT Selection Mechanism**: By default, Fireblocks selects the first 250 smallest confirmed inputs available for a specified vault account. This helps customers use up small UTXOs and reduce the number of available UTXOs in their vault account. 2. **Configurable Selection Mechanism**: Fireblocks can be configured to select the 250 largest confirmed inputs first. This configuration requires opening a request with our support team. This consolidation transaction is typically made from the Deposits vault account to one of the Withdrawals vault accounts to maintain a sufficient asset balance for user withdrawals. However, the transaction can also be executed with multiple destinations, such as different vault accounts, exchange accounts, external addresses, or even a cold wallet. The trigger for the consolidation process can be based on the number of deposits made into the Deposits vault account. For instance, customers can decide that after every 250 deposits (tracked by an internal counting logic), the system will automatically create the consolidation transaction. Others may choose to execute this transaction based on current network fees to save on transaction costs. Fireblocks provides two important endpoints to help clients build their UTXO consolidation logic: 1. [Get Maximum Spendable Amount in a Single Transaction](/reference/getmaxspendableamount): This endpoint returns the maximum amount of a particular asset that can be spent in a single transaction from a specified vault account. 2. [Create Transaction](/reference/createtransaction): This endpoint creates a transaction with the specified amount returned from the endpoint above. Additionally, clients who want to trigger the consolidation transaction based on the number of deposits made into their Deposits vault account can use the Fireblocks [Webhook service](/reference/configure-webhook-urls) to get notifications on completed incoming transfers. For more details, see the [UTXO Consolidation Developer Guide](/reference/consolidate-utxos). ## Process Account Based Asset Deposits Customers who want to generate a unique address for each of their end-users need to create a new intermediary vault account for each user and then the required vault wallet within this newly generated vault account. This is necessary because account-based assets do not support the generation of multiple addresses under the same wallet. Once the generated address is shared with the end-user and the end-user makes a deposit into that vault account, most customers will want to perform a sweeping operation. This operation consolidates all deposited funds from the intermediary vault accounts into a single treasury management vault account for various business purposes. Sweeping funds involves moving assets from address A to address B. It is important to note that this is an on-chain operation and includes transaction fee payments. For more details, see the [Sweeping Mechanism](/docs/sweep-funds). While sweeping base assets like ETH is straightforward, sweeping deposited ERC20 tokens presents a challenge. This is because the transaction fee must be paid in the network's base asset, which end-users might not have in their wallets. To address this issue, use the Fireblocks [Gas Station](/docs/work-with-gas-station) to fuel each deposit vault account with the base asset, or use [gasless transactions](/docs/gasless-transactions) so a relay pays the fee and deposit vault accounts never hold the base asset. See [Gas Management](/docs/gas-management) to compare the two. ### Memo and tag-based deposit identification For account-based chains, we recommend creating a dedicated vault account for each end user and managing all of their associated wallets within that account. Some chains, such as Stellar and Ripple, support deposit identification using a memo or tag (see the full list [here](/reference/vault-objects#createaddressresponse)). On these chains, you can assign each end user a unique memo or tag to identify their deposits using the [Create a new deposit address](/reference/createvaultaccountassetaddress) endpoint. This allows multiple users to deposit into a shared wallet without needing a separate vault account or wallet per end user. Deposits are credited by matching the memo or tag field included in the transaction. This shared-wallet approach reduces operational overhead and fees by eliminating the need for sweeping funds and allowing reconciliation through a single or small number of transactions. However, it is still considered good practice to separate vault accounts by function. For example, cold storage, warm wallets, deposits, withdrawals, and treasury. **Important** The main disadvantage of this approach is that you must manage internal credits and balances based solely on transactions, because there is no on-chain balance to validate. Your internal system becomes the source of truth. ## Create Deposit Addresses Best Practices Customers serving hundreds of thousands or even millions of users will eventually need to create a corresponding number of deposit addresses within a single vault account (for UTXO deposits) and/or intermediate vault accounts (for Account Based assets) in their workspace. A basic implementation is to create a new deposit address or vault account for the end-user whenever the user registers for the service or requests a deposit address for a specific coin. In this setup, customers make a real-time API call to Fireblocks to generate a new deposit address or create a new vault account with a vault wallet for the requested asset, triggered by the end-user request. While this approach is acceptable, the best practice we recommend is to create these deposit addresses or intermediate vault accounts proactively. Fireblocks customers should run a service that continuously creates addresses, vault accounts, and vault wallets, regardless of user requests. This proactive approach means maintaining a pool of pre-generated vault accounts and addresses. When a user requests a new deposit address, customers can quickly assign a pre-generated address instead of making a real-time API call to Fireblocks. This reduces the risk of encountering API errors in real-time and significantly decreases the waiting time for users to receive a new address, thereby enhancing the user experience and providing a near real-time service. ### Pre-creating vault accounts We recommend pre-creating vault accounts in advance. When an end user requests access, you can create the required wallets later and assign them to one of the pre-created vault accounts. To manage scale efficiently, you can maintain either high or low vault account thresholds — once the number of available accounts reaches a low threshold, resume creating accounts until the required threshold is met. When a wallet is created for an end user, assign it to the appropriate pre-created vault account and update the assignment in your internal systems. ### Resource identification To map resources to the correct end users, we recommend using the following fields: * **Vault account:** use the `name` field * **Wallet:** use the `description` field Assign these fields at resource creation time and use them internally for end-user assignment. **Important** Do not store personally identifiable information (PII) in these fields. ### Vault account visibility When creating deposit vault accounts, set the `hiddenOnUi` flag to `true`. This helps ensure proper management of important vault accounts in the Fireblocks Console. ### Wallets requiring on-chain creation Some wallets require an on-chain operation to be created, including: * SPL tokens (Solana) * Ripple tokens * Stellar tokens * HBAR * EOS For SPL tokens, Associated Token Accounts (ATAs) can be created by receiving a token transfer from an external source. The account creation fee is included as part of that transfer. While SPL, Ripple, and Stellar tokens use fees taken from the wallet itself, EOS wallets are currently funded by Fireblocks treasury and are expected to be used reasonably. In the future, the funding for these wallets will transition to a self-bootstrapped model. To associate the pre-generated address with the end-user, during the assigning process on the customer's side, the following APIs should be called to synchronize both Fireblocks and the customer's internal system with the user reference: 1. **UTXO and Tag/Memo Based Assets**: When assigning a new UTXO-based asset deposit address within your Deposit vault account for a new user, call the [Update Address Description API endpoint](/reference/updatevaultaccountassetaddress) with the newly assigned user reference as the description. 2. **Account-Based Assets**: When assigning a new intermediate vault account for an end-user, call the [Rename a Vault Account API endpoint](/reference/updatevaultaccount) with the newly assigned user reference as the vault account name. Ensure that the vault account name does not contain any PII data. ## Validate Balances Fireblocks allows you to receive notifications about incoming transactions to your Fireblocks vault accounts. A common setup for Fireblocks customers is to subscribe to the webhook service and get notified whenever a new deposit is made into a vault account. When an event for an incoming transaction is triggered, followed by an event confirming the deposit's completion, the typical setup on the customer's end will automatically update the internal ledger and adjust the balance of the user associated with the deposited vault account. While this approach is generally correct, Fireblocks recommends implementing additional validation steps before crediting the end user with the deposited amount to avoid potential loss of funds. > [!WARNING] > > **Validating Incoming Balances is crucial!** Learn more about the best practices for validating balances and performing reconciliation processes in the following [guide](/docs/validate-balances) ## Maintain an Internal Ledger An internal ledger is a fundamental component to consider when building a retail-facing solution. Essentially, it records incoming transfers and balances for your users. Maintaining this ledger is crucial because funds deposited by users into their accounts are often swept or moved to a different account, as described in the sweeping mechanism section. You need to accurately track how much each user has deposited and what balance should be displayed in your system. Fireblocks does not provide a built-in component or feature for this specific need, so customers can implement it in any way they see fit. However, Fireblocks offers all the necessary utility tools and features to effectively maintain such a ledger. This includes APIs for validation and reconciliation processes and a webhook service that allows you to create an event-driven system triggered by various events in your Fireblocks workspace. # Overview Source: https://developers.fireblocks.com/docs/manage-users Fireblocks users can be added either via the web console or via the API: * For adding users via the console please refer to [this guide](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles) * For adding users via the API please refer to the following [API endpoints](/reference/createconsoleuser). Generally, there are different user roles that can be assigned to a specific user, please see the user role table below: There are different API Key types. Each type contains other capabilities in addition to transaction permissions. | API Key Type | Role | Transaction Permissions | Environment | | - | - | - | - | | **Admin** | Signing | Can sign transactions. | Production | | **Non-Signing Admin** | Non-Signing | Can't sign transactions. | Production + Sandbox | | **Signer** | Signing | Can sign transactions. | Production | | **Approver** | Non-Signing | Can't sign transactions. | Production | | **Editor** | Non-Signing | Can't sign transactions. | Production + Sandbox | | **Viewer** | View-Only | Can only view transaction history. | Production + Sandbox | | **Security Auditor** | View-Only | Can only view transaction history and workspace elements. | Production + Sandbox | | **NCW\_ADMIN** | Non-Signing | Can only manage Non Custodial Wallets. | Production + Sandbox | | **NCW\_SIGNER** | Signing | Can only sign transaction from Non Custodial Wallets. | Production + Sandbox | > [!NOTE] > > **Initiating vs. signing transactions.** "Can't sign transactions" refers only to the signing step. Non-Signing Admin and Editor can still initiate transactions through the Fireblocks Console or API, for example, by calling `POST /transactions`. Approver cannot initiate transactions, only approve them. When Non-Signing Admin or Editor initiates a transaction, a separate signer (Admin, Signer, or NCW\_SIGNER) designated by the workspace [Transaction Authorization Policy (TAP)](/docs/set-transaction-authorization-policy) signs it. This lets you segregate the initiator, approver, and signer across different API users. See [Segregate Duties](/docs/segregate-duties) for common role splits. Defining users in your workspace may seem straightforward, but it requires careful alignment with the supported roles. Whether your goal is maximum security or optimal work efficiency, the right approach depends on your specific business use cases. Before getting started, familiarize yourself with [user groups](https://support.fireblocks.io/hc/en-us/articles/29184024915356-User-group-management) and [approval groups](https://support.fireblocks.io/hc/en-us/articles/29184265622556-Approval-groups). These two features are crucial for defining user roles and actions in your Fireblocks workspace and can significantly enhance your operational efficiency and security. * Segregate users into two categories: those responsible for managerial decisions and changes, and those handling day-to-day operations. Differentiating between users who can make changes in the workspace and those who can only initiate or sign transactions will enhance security. For more details, refer to [this guide](/docs/segregate-duties). * Always consider the admin quorum approval. The more approvals required for a workspace change, the longer and more complex the implementation process will be. * Start by defining groups and their tasks. Based on these tasks, identify the roles that match and add individual users accordingly. Managing your operations with the correct setup of groups will enhance the performance of regular operations and facilitate general workspace changes. Follow the general best practices when choosing users and roles as outlined [here](https://support.fireblocks.io/hc/en-us/articles/5254222799900-Best-practices-for-choosing-user-roles). ## API Users Use Cases API users can be used either to access Fireblocks API or to be paired with a cosigner (one can be both at the same time, but this is not the best practice). In the case of an API user used to access the API, it has API key and API Secret that are required for authentication. In the case of an API user paired with a cosigner, it has MPC key shares if it's a Signer or Admin, a One-time pairing token, and Device in case it's an API user that is paired with a device. In the case of an API user used for KeyLink, it does not have MPC keys, but it has the pairing token, the access token, and the device. # Manage Withdrawals at Scale Source: https://developers.fireblocks.com/docs/manage-withdrawals-at-scale # Withdrawal Flow Withdrawals are one of the most critical and active functions in any retail-facing crypto business. Users depend on the withdrawal process to move funds quickly and securely. Designing an efficient, secure withdrawal flow is essential to delivering a reliable platform. ## Automate the withdrawal flow ### Setup Automation is essential for ensuring a smooth, uninterrupted 24/7 withdrawal service. Relying on manual transaction signing is not scalable and introduces significant security risks. To address these challenges, implement a [Fireblocks Co-signer](/cosigners/overview/cosigner-architecture-overview) component. This approach guarantees continuous operation and enhances the overall security of your withdrawal process. ### Highly available setup One best practice for automation is setting up a highly available system by implementing the Co-signer in active/active mode. In this configuration, systems can take over from one another without downtime, ensuring that failover capability is always in place. For more information, see [Fireblocks Co-signer High Availability setup](/cosigners/operations/multiple-cosigners-high-availability). ### Co-Signer logging and monitoring Comprehensive monitoring and alerting are vital for maintaining a secure and reliable automated signing process. Setting up these systems lets you track transaction status in real time and detect anomalies that may indicate potential issues. Configure alerts to notify relevant personnel immediately when transactions are stuck or suspicious activity is detected, enabling prompt intervention. For more information, see [API Co-signer Maintenance](/cosigners/operations/api-cosigner-maintenance). ## Implementing multiple withdrawal wallets ### Withdrawal pool Using multiple withdrawal vault accounts reduces the impact of stuck transactions and enables higher throughput. Use [Wallet Pools](/docs/wallet-pools) to do this: group your withdrawal vault accounts into a Wallet Pool, then send each withdrawal with the pool as the source (`source.type: WALLET_POOL`). Fireblocks selects a member for each transaction, prefers members that are not congested, and rotates across the rest, so you don't build your own routing. A pool distributes transactions, not funds. Keep the following in mind: * **Balance monitoring:** Each member must hold the full amount of any withdrawal it sends, because balances are not combined across members. Monitor the balance of each member. * **Automated refills:** Fireblocks does not move funds between members. Implement automated refills to transfer funds between them as needed. * **Non-EVM chains:** Congestion-aware rotation runs on EVM chains only. On other chains, including Bitcoin, the pool selects a member by balance instead of rotating. To spread withdrawals on those chains, distribute them across withdrawal vault accounts yourself using round-robin (a circular execution pattern between wallets). Multiple withdrawal wallets are particularly beneficial for Ethereum, which uses the nonce mechanism and therefore processes transactions sequentially, and for UTXO-based assets like Bitcoin. In the UTXO model, an unconfirmed output from a previous transaction can be used as input for a new one. This can result in a chain of stuck transactions if the first is not confirmed. Although mechanisms like [Replace By Fee (RBF)](/docs/boost-transactions#rbf-and-cpfp-in-fireblocks) and [Child Pays For Parent (CPFP)](/docs/boost-transactions#rbf-and-cpfp-in-fireblocks) exist to release stuck transactions, relying on them is not ideal. The Bitcoin protocol also has a built-in mechanism that rejects transactions using an unconfirmed output from a transaction with more than 25 predecessors. This can create a queue of withdrawals and, in some cases, cause withdrawal failures. ### Number of withdrawal wallets A typical ECDSA-based chain transaction takes 6–7 seconds from initiation to broadcast. EdDSA-based chain transactions are slightly faster by about a second. On chains other than Solana and Tron (where parallel processing is not supported), Fireblocks serializes and signs transactions only after observing the previous transaction on the chain or node (TAP evaluation is still performed in parallel). In some cases, there is an additional delay of approximately 2 seconds before the transaction or its artifact is observed after submission. For each sustained 1 TPS, we recommend using 5–6 withdrawal wallets as members of a Wallet Pool, so each new withdrawal goes to a different wallet. This guidance applies to steady-state throughput rather than short-term peaks. Queued transactions are acceptable as long as the queue continues to clear. > [!WARNING] > > **Pay close attention to transaction queues.** Rapid growth may indicate that the Co-Signer is not signing transactions fast enough — this can be caused by slow callbacks, underpowered machines, or insufficient Co-Signers. If transactions remain queued for more than two hours, cancel and resubmit them. Transactions stuck before signing will fail when the timeout is reached. > [!NOTE] > > Two hours is the workspace default. You can set a custom timeout at the workspace, signer, or per-transaction level. See [Transaction Timeout Configuration](https://support.fireblocks.io/hc/en-us/articles/27174908726044-Transaction-Timeout-Configuration). > [!TIP] > > **Let Account Traffic Control (ATC) watch queues for you.** Instead of polling for stuck transactions, ATC monitors your pending transactions and fires a `transaction.alert.stuck` webhook automatically when one stalls. See [Account Traffic Control](https://support.fireblocks.io/hc/en-us/articles/27129539023004-Account-Traffic-Control). For wallets that support parallel processing (specifically Solana and Tron), enable parallel processing for up to five concurrent transactions. This configuration typically requires two withdrawal wallets per 1 TPS. | Chain type | Recommended withdrawal wallets | Notes | | - | - | - | | EVM / ECDSA-based | 5–6 per 1 TPS | Use a Wallet Pool; monitor for stuck nonce chains | | Bitcoin (UTXO) | 2–3 per 1 TPS | Batch in 30-second windows; stay under 25-tx chain limit | | Solana / Tron | 2 per 1 TPS | Enable parallel processing (up to 5 concurrent tx per wallet) | | Stellar | \~10 per 1 TPS | One tx per account per ledger (5s intervals) | ## Blockchain-specific guidance ### EVMs Fireblocks supports advanced nonce management for EVMs to improve transaction latency and throughput. If a transaction fails and is not mined, its nonce can be reused by the next transaction from the same wallet, preventing the transaction chain from getting blocked. Monitor transactions closely and identify any stuck in a confirming state from the same wallet. This is usually caused by a stuck low-fee transaction. Boosting or replacing the transaction via RBF allows the next transaction to be pushed to the chain. Identifying such situations early is critical; otherwise, you may submit multiple transactions with low fees, causing a backlog. > [!WARNING] > > **Watch for delays due to fee spikes.** Some node implementations enforce per-address submission limits when transactions have low fees, long nonce chains, or unconfirmed dependencies. Although the Fireblocks framework can retry dropped transactions, delays during fee spikes can cause low fees and additional stuck transactions. We recommend grouping several withdrawal accounts into a [Wallet Pool](/docs/wallet-pools), which routes withdrawals across them. Using five to six wallets enables approximately 1 TPS throughput. ### UTXOs Under the current design, a UTXO wallet can serialize a single transaction at a time. In some cases, unconfirmed change outputs are reused in subsequent transactions, which can result in a stuck wallet if: * A transaction is not mined due to low fees. * The dependency chain reaches the maximum chain length of 25 unconfirmed transactions (common in Bitcoin nodes), causing new transactions to be rejected. To avoid this situation, use one or more of the following methods: * Batch withdrawals using multi-destination transactions. Creating a batch every 30 seconds results in 20 transactions over 10 minutes, staying below the 25-transaction limit. * Schedule sweeping or funding to withdrawal wallets to obtain multiple confirmed UTXOs, reducing reliance on unconfirmed change outputs. * Use CPFP to release transactions stuck due to low fees. Apply CPFP before reaching the 25-transaction chain limit. (RBF support is planned for the future.) * Use multiple withdrawal wallets, as described earlier. In general, aim to balance the number of UTXOs. Too many UTXOs are harder to reconcile; too few can cause mempool congestion and unconfirmed transactions. For Bitcoin, we recommend holding two to three withdrawal wallets and batching withdrawals in 30-second windows. For additional details on UTXO selection logic and filtering, see [Select UTXOs](/reference/select-utxos-for-a-transaction). ### Solana and Tron On Solana, Fireblocks supports parallel transaction serialization per wallet using durable nonces, with up to 1,000 concurrent transactions in non-finalized states. Fee payer transactions and support for transactions without durable nonces are planned for the future. For Tron, Fireblocks supports parallel serialization of up to 5 transactions per wallet, with support for up to 600 concurrent non-finalized transactions. > [!WARNING] > > **You need multiple Co-signers for high-throughput setups.** High-throughput setups require multiple Co-signers that can handle parallel traffic. Additional Co-signers can be configured as needed. ### Stellar Stellar uses sequence numbers and allows only one transaction per account per ledger (created every 5 seconds). As a result, throughput from a single wallet is limited. We recommend creating approximately 10 Stellar withdrawal wallets per 1 TPS. Fireblocks is considering support for channel accounts to enable better throughput and latency. ## Confirmation policy Blockchain confirmations verify transactions while preventing fraud and double-spending. However, the confirmation threshold directly affects how quickly funds become available: the higher the threshold, the longer the delay. For internal operations like sweeping or reconciliation, you can define a low confirmation requirement. Setting confirmations to `0` means: * On **Bitcoin-based chains and EVMs**: the transaction is considered complete once it enters the mempool. * On **other chains**: the transaction is considered complete immediately once included in a block. On Bitcoin-based UTXO chains, setting confirmations to `0` also allows the resulting UTXOs to be immediately used in subsequent transactions. For more information, see [Deposit Control & Confirmation Policy](/docs/define-confirmation-policy). ## Transaction batching Transaction batching lets you consolidate multiple withdrawal requests into a single transaction with multiple destinations, minimizing fees. Because a batched transaction carries a single set of metadata, it is more cost-effective than multiple individual transactions. Fireblocks supports transaction batching for UTXO-based assets, Solana, and SPL20 tokens. Batching does require collecting multiple user requests over a set period (for example, every X minutes) before processing them together. This approach reduces fees but may introduce slight delays for users expecting immediate withdrawals. The right batching interval depends on your withdrawal volume and user experience requirements. Fireblocks supports creating a single transaction with multiple destinations using the [Create a new transaction](/reference/createtransaction) endpoint by passing the `destinations` array, which contains separate `destination` objects. For more information, see [Multiple destination transfer](/transactions/transfers#send-to-multiple-destinations). ## Working with One-Time Addresses By default, outgoing transactions in Fireblocks can only be made to whitelisted addresses approved by the Workspace's Admin Approval Quorum. For businesses looking to scale their withdrawal flows and eliminate manual processes, this can become cumbersome. To address this, Fireblocks introduced the One-Time Address (OTA) feature. OTA allows you to interact with non-whitelisted addresses, streamlining the withdrawal process. For more information, including security considerations, see [Work with One-Time Addresses](/docs/whitelist-addresses#work-with-one-time-addresses). ## Idempotent transactions In RESTful APIs, idempotency means that making the same API call multiple times produces the same result as making it once. For financial operations like withdrawals, this is critical; without it, repeated requests can result in duplicate transactions and cause significant discrepancies. Fireblocks provides idempotency for transaction creation through the `externalTxId` parameter. When you include `externalTxId` in a [Create a new transaction](/reference/createtransaction) request, Fireblocks rejects any subsequent request using the same value with an HTTP 400 error: ```json theme={"system"} { "message": "The external tx id that was provided in the request, already exists", "code": 1438 } ``` Unlike the `Idempotency-Key` header (which expires after 24 hours), `externalTxId` is stored permanently. Fireblocks rejects duplicate values even if the follow-up request is made more than 24 hours after the original. For more information, see [Transaction idempotency](/reference/api-idempotency#transaction-idempotency). # Overview Source: https://developers.fireblocks.com/docs/network-link-integration-guide-for-provider-connectivity > [!NOTE] > > **Want to know more about the Provider Network and commercial prerequisites?** Visit [our Help Center article about the Provider Network](https://support.fireblocks.io/hc/en-us/articles/27221291025308-About-the-Provider-Network) to learn more. > > See the [Fireblocks Provider Connectivity API v2 documentation](https://fireblocks.github.io/fireblocks-network-link/v2/docs.html) on GitHub to learn how to connect your service to Fireblocks. # Registering as a service provider > [!NOTE] > > **Note on the supported testing scope.** As a best practice, we recommend that your onboarding should cover only up to three stages: **Dev** → **Staging** → **Production**. Additional testing setups for supporting multiple internal environments are not supported by default and require explicit approval. Once your license agreement for listing your service with Fireblocks is signed, the Fireblocks technical team will request the following connected account registration settings: 1. Display name (e.g., "My Exchange" or "My Exchange Sandbox") 2. Icon (a 32x32 .svg file) 3. Step-by-step guide for generating an API key for your platform 1. A public link to your knowledge base is preferred. A shareable document or PDF is also acceptable. 4. Base URL for your API endpoints (e.g., "[https://my-service.com/fireblocks](https://my-service.com/fireblocks)") 5. Will your service provide a sandbox environment? 6. For Off-Exchange capabilities, please provide your CSR file. 7. User credentials for testing 8. Which signing method will your server use for all requests? Please choose one of the supported options by specifying the pre-encoding function, signing algorithm, and post-encoding function during the server onboarding process. 1. **Pre-encoding options:** 1. `URL encoded` 2. `Base64` 3. `HexStr` 4. `Base58` 5. `Base32` 2. **Signing algorithms & possible hash functions:** 1. `HMAC (SHA512, SHA3_256, or SHA256)` 2. `RSA PKCS1v15 (SHA512, SHA3_256, or SHA256)` 3. `ECDSA prime256v1/secp256k1 (SHA256 only)` 3. **Post-encoding options:** (URL encoding not supported) 1. `Base64` 2. `HexStr` 3. `Base58` 4. `Base32` 9. [Validation tool results](https://github.com/fireblocks/fireblocks-network-link/tree/main/v2/api-validator) # Asset Mapping in Network Link v2 For Network Link v2 integrations, asset mapping is based on the provider's response to `GET/capabilities/assets`. The required fields are: * ID (on the provider's side) * Name (on the provider's side) * Symbol (on the provider's side) * Decimal places (number of decimals for the asset on the provider's side) * Test asset (true/false) * Type (BucketAsset, ERC20Token, BEP20Token, StellarToken, Jetton, SPLToken) # IP allowlisting All API calls from Fireblocks to your service are sent from a fixed set of IP addresses, grouped by geographical region. Your service should allowlist these addresses to allow Fireblocks communication with your servers. | Singapore | Europe | USA | | - | - | - | | 18.99.36.0 18.99.36.1 18.99.36.2 18.99.36.3 18.99.36.4 18.99.36.5 18.99.36.6 18.99.36.7 18.99.36.8 18.99.36.9 52.76.208.129 | 18.98.161.0 18.98.161.1 18.98.161.2 18.98.161.3 18.98.161.4 18.98.161.5 18.98.161.6 18.98.161.7 18.98.161.8 18.98.161.9 18.98.161.10 18.98.161.11 18.98.161.12 18.98.161.13 18.98.161.14 18.98.161.15 18.98.161.16 18.98.161.17 18.98.161.18 18.98.161.19 18.133.153.74 3.10.68.107 3.64.123.47 18.158.242.74 3.10.103.242 3.67.233.15 | 18.97.132.0 18.97.132.1 18.97.132.2 18.97.132.3 18.97.132.4 18.97.132.5 18.97.132.6 18.97.132.7 18.97.132.8 18.97.132.9 40.117.39.160 | # Mandatory endpoints implementation per user flow ## Minimum viable provider * **Required`GET /capabilities` response:** "accounts": \["\*"] * **Required endpoints:** * `GET /capabilities` * `GET /capabilities/assets` * `GET /accounts` * `GET /accounts/{accountId}` ## Balance support * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"] * **Required endpoints:** * `GET /accounts/{accountId}/balances` ## Transfer support * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "transfers": \["\*"] * **Required endpoints:** * `GET /accounts/{accountId}/capabilities/transfers/deposits` * `GET /accounts/{accountId}/capabilities/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/deposits` * `GET /accounts/{accountId}/transfers/deposits/{id}` * `GET /accounts/{accountId}/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/withdrawals/{id}` ## Blockchain transfer * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "transfers": \["*"], "transfersBlockchain": \["*"] * **Required endpoints:** * `GET /accounts/{accountId}/capabilities/transfers/deposits` * `GET /accounts/{accountId}/capabilities/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/deposits` * `GET /accounts/{accountId}/transfers/deposits/{id}` * `GET /accounts/{accountId}/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/withdrawals/{id}` * `POST /accounts/{accountId}/transfers/deposits/addresses` * `GET /accounts/{accountId]/transfers/deposits/addresses` * `GET /accounts/{accountId}/transfers/deposits/addresses/{id}` * `POST /accounts/{accountId}/transfers/withdrawals/blockchain` ## Peer transfer * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "transfers": \["*"], "transfersPeerAccounts": \["*"] * For fiat assets, add "transfersFiat": \["\*"] to the response. * **Required endpoints:** * `GET /accounts/{accountId}/capabilities/transfers/deposits` * `GET /accounts/{accountId}/capabilities/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/deposits` * `GET /accounts/{accountId}/transfers/deposits/{id}` * `GET /accounts/{accountId}/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/withdrawals/{id}` * `POST /accounts/{accountId}/transfers/withdrawals/peeraccount` ## Internal transfer * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "transfers": \["*"], "transfersInternal": \["*"] * For fiat assets, add "transfersFiat": \["\*"] to the response. * **Required endpoints:** * `GET /accounts/{accountId}/capabilities/transfers/deposits` * `GET /accounts/{accountId}/capabilities/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/deposits` * `GET /accounts/{accountId}/transfers/deposits/{id}` * `GET /accounts/{accountId}/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/withdrawals/{id}` * `POST /accounts/{accountId}/transfers/withdrawals/subaccount` ## Fiat transfer * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "transfers": \["*"], "transfersFiat": \["*"] * **Required endpoints:** * `GET /accounts/{accountId}/capabilities/transfers/deposits` * `GET /accounts/{accountId}/capabilities/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/deposits` * `GET /accounts/{accountId}/transfers/deposits/{id}` * `GET /accounts/{accountId}/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/withdrawals/{id}` * `POST /accounts/{accountId}/transfers/withdrawals/fiat` ## Liquidity/RFQ support * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "liquidity": \["\*"] * **Required endpoints:** * `GET /capabilities/liquidity/quotes` * `POST /accounts/{accountId}/liquidity/quotes` * `GET /accounts/{accountId}/liquidity/quotes` * `GET /accounts/{accountId}/liquidity/quotes/{id}` * `POST /accounts/{accountId}/liquidity/quotes/{id}/execute` ## On-/Off-Ramp & Bridging * **Required`GET /capabilities` response:** "accounts": \["*"], "ramps": \["*"], "balances": \["\*"] * For prefunded flows, "balances": \["\*"] must be returned with `GET` account API. See below. * **Required endpoints:** * `GET /accounts/{accountId}/capabilities/ramps` * `POST /accounts/{accountId}/ramps` * `GET /accounts/{accountId}/ramps` * `GET /accounts/{accountId}/ramps/{id}` * `GET /accounts/{accountId}/balances` (only for prefunded flows) * `GET /accounts/{accountId}/rate` * Your service should implement the `rate` endpoint to ensure Fireblocks customers see your service's real-time rates for asset pairs. Otherwise, Fireblocks defaults to quotes or its internal rating service. ## Collateral > [!NOTE] > > In addition to implementing the endpoints below, providers supporting Collateral/Off-Exchange flows must also integrate with the Fireblocks Off-Exchange API. This is required for reporting events from the provider back to Fireblocks (e.g., confirming deposits or settlements). > > Technical Reference: [Fireblocks Off-Exchange - Exchange to Fireblocks](https://fireblocks.github.io/fireblocks-off-exchange/#tag/Exchange-to-Fireblocks) The provider should also use this [endpoint](https://fireblocks.github.io/fireblocks-off-exchange/#tag/Exchange-to-Fireblocks). * **Required`GET /capabilities` response:** "accounts": \["*"], "balances": \["*"], "transfers": \["*"], "transfersBlockchain": \["*"], "collateral": \["\*"] * **Required endpoints:** * `GET /accounts/{accountId}/balances` * `GET /accounts/{accountId}/capabilities/transfers/deposits` * `GET /accounts/{accountId}/capabilities/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/deposits` * `GET /accounts/{accountId}/transfers/deposits/{id}` * `GET /accounts/{accountId}/transfers/withdrawals` * `GET /accounts/{accountId}/transfers/withdrawals/{id}` * `POST /accounts/{accountId}/transfers/deposits/addresses` * `GET /accounts/{accountId}/transfers/deposits/addresses` * `GET /accounts/{accountId}/transfers/deposits/addresses/{id}` * `POST /accounts/{accountId}/transfers/withdrawals/blockchain` * **Collateral endpoints:** * `POST accounts/{accountId}/collateral/link` * `GET accounts/{accountId}/collateral/link` * `POST accounts/{accountId}/collateral/{collateralId}/addresses` * `GET accounts/{accountId}/collateral/{collateralId}/addresses` * `GET accounts/{accountId}/collateral/{collateralId}/addresses/{id}` * `POST accounts/{accountId}/collateral/{collateralId}/intents/deposits` * `POST accounts/{accountId}/collateral/{collateralId}/deposits` * `GET accounts/{accountId}/collateral/{collateralId}/deposits` * `GET accounts/{accountId}/collateral/{collateralId}/deposits/{collateralTxId}` * `POST accounts/{accountId}/collateral/{collateralId}/intents/withdrawals` * `POST accounts/{accountId}/collateral/{collateralId}/withdrawals` * `GET accounts/{accountId}/collateral/{collateralId}/withdrawals` * `GET accounts/{accountId}/collateral/{collateralId}/withdrawals/{collateralTxId}` * `POST accounts/{accountId}/collateral/{collateralId}/settlement` * `GET accounts/{accountId}/collateral/{collateralId}/settlement` * `GET accounts/{accountId}/collateral/{collateralId}/settlements/{settlementVersion}` # Notabene Source: https://developers.fireblocks.com/docs/notabene Connect Notabene, then validate, encrypt, and attach Travel Rule data to outgoing transactions through the Fireblocks API. Notabene is a Travel Rule provider. It is a premium add-on that Fireblocks Support enables on Developer Sandbox, Testnet, and Mainnet workspaces. You connect it and build its policy in the Console, and send Travel Rule data through the Fireblocks API. Fireblocks does not store Travel Rule data permanently and does not hold the decryption keys. ## What you can do through the API | Step | Task | Console | API | | - | - | - | - | | 1 | Get your VASP details | No | [Get VASP details](/api-reference/travel-rule/get-vasp-details) | | 1 | Register your PII encryption key | No | [`PUT /screening/travel_rule/vasp/update`](/api-reference/travel-rule/add-jsondidkey-to-vasp-details) | | 1 | Assign or remove a VASP DID for a vault account | No | [Assign VASP to vault](/api-reference/travel-rule/assign-vasp-to-vault) | | 1 | Get the VASP DID assigned to a vault account | No | [Get assigned VASP to vault](/api-reference/travel-rule/get-assigned-vasp-to-vault) | | 1 | Connect Notabene | Yes | No | | 2 | Build the Trigger (Screening Policy) and Outcome (Post-Screening Policy) | Yes | No | | 3 | Set up the Fireblocks SDK in your system | No | No API call; install the TypeScript SDK | | 4 | Validate a transaction's Travel Rule data | No | [`POST /screening/travel_rule/transaction/validate/full`](/api-reference/travel-rule/validate-full-travel-rule-transaction) | | 4 | Search the Notabene VASP directory | No | [`GET /screening/travel_rule/vasp`](/api-reference/travel-rule/get-all-vasps) | | 4 | Attach the Travel Rule message to a transaction | No | `travelRuleMessage` in [`POST /transactions`](/api-reference/transactions/create-a-new-transaction) | To read a transaction's Notabene result, use the `tr` field; see [Transaction Screening Operations](/docs/transaction-screening-operations). ## Step 1: Connect Notabene You need a Notabene account. By default, your Notabene production environment connects to your Fireblocks production workspace, and your Notabene sandbox to your Fireblocks sandbox workspace; for any other pairing, contact Fireblocks Support. 1. Get your VASP DID, your Decentralized Identifier in the Notabene network, with Get VASP details. 2. Create an Ed25519 DID key with the Notabene CLI. Other VASPs use its public key to encrypt PII sent to you. See [Notabene's Fireblocks integration docs](https://devx.notabene.id/docs/using-notabene-and-fireblocks). 3. Register the key on your VASP details. 4. Assign your VASP DID to every receiving vault account. Incoming transactions to a vault account without an assigned DID cannot be screened. 5. In the Console, connect Notabene with your Notabene API key, secret, and VASP DID. You receive an email once the connection is approved. 6. In your Notabene dashboard, set the Notabene status a transaction must reach before Fireblocks treats screening as complete. To switch to new API keys, submit a ticket to Fireblocks Support with the new API key, secret, and VASP DID. ## Step 2: Build the policy Build the Trigger and Outcome in the Console; default rules apply until you create your own. See [Notabene](https://support.fireblocks.io/hc/en-us/articles/30614346879900-Notabene) on the Help Center. Notabene supports every Outcome action: Accept, Reject, Alert, Freeze, Wait, and Cancel. Outcome rules act on Notabene's status: | Status | Meaning | | - | - | | Completed | Screening finished successfully. | | Pending | Still in progress. Accept the transaction as it is, or wait up to four hours for a result before it is canceled automatically. | | Rejected | The beneficiary VASP was not recognized, or declined the request. | | Failed | Screening could not complete, for example because of incomplete Travel Rule data or a Notabene error. | | Blocking time expired | The time configured for holding the transaction ran out. | | Canceled | Screening was canceled. For outgoing transactions, you can choose whether to proceed anyway. | ## Step 3: Set up the Fireblocks SDK Install the Fireblocks SDK in your system; Travel Rule data is validated and attached from your code. See [TypeScript SDK](/reference/typescript-sdk). ## Step 4: Send an outgoing transaction An outgoing transaction without a `travelRuleMessage` bypasses Travel Rule screening. 1. **Validate the Travel Rule data** with the full validation endpoint, passing the transaction details and the originator and beneficiary data you have. Set the `notation` query parameter explicitly: it defaults to `notabene`, and the default will change to `fireblocks`, which translates asset tickers and amounts automatically. The basic Validate Travel Rule Transaction endpoint is being deprecated. ```json theme={"system"} { "transactionAsset": "ETH", "transactionAmount": "10000000000000000000", "originatorVASPdid": "did:ethr:0x44957e75d6ce4a5bf37aae117da86422c848f7c2", "beneficiaryVASPdid": "did:ethr:0x47463999eb42dc2aaacb29624c512603221227a1", "transactionBlockchainInfo": { "destination": "0xDB6A31EC49D5FB35EF6BA6CE0A3B071C8BA7F7F0" }, "originator": {}, "beneficiary": {} } ``` | Response field | Values | | - | - | | `isValid` | `true` when all required data is present. | | `type` | `BELOW_THRESHOLD`, `TRAVELRULE`, or `NON_CUSTODIAL`. If `BELOW_THRESHOLD`, you do not need Travel Rule data. | | `beneficiaryAddressType` | `HOSTED`, `UNHOSTED`, or `UNKNOWN`. | | `addressSource` | How the beneficiary address was identified, for example `ADDRESS_GRAPH`. `UNKNOWN` if it was not. | | `beneficiaryVASPdid`, `beneficiaryVASPname` | The identified beneficiary VASP. | | `warnings` | Beneficiary data that is missing and needs to be collected. | 2. **Identify the beneficiary VASP** if it was not found automatically: search the VASP directory, for example `?search=Fireblocks`, and pass its DID as `beneficiaryVASPdid`. 3. **Collect the missing data** listed in `warnings`, with Notabene's widget or API, and validate again until `isValid` is `true`. See [Notabene's validation documentation](https://devx.notabene.id/docs/validation-api). 4. **Encrypt the PII** with the [Notabene PII SDK](https://devx.notabene.id/recipes/using-the-pii-sdk-and-fireblocks-api): end-to-end, which only the counterparty VASP can decrypt, or hybrid, which Notabene can decrypt as well. 5. **Create the transaction** with the encrypted message as `travelRuleMessage`: ```ts theme={"system"} const { data } = await fireblocks.transactions.createTransaction({ transactionRequest: { assetId: "ETH", amount: "10", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xDB6A31EC49D5FB35EF6BA6CE0A3B071C8BA7F7F0" }, }, travelRuleMessage: { originatorVASPdid: "did:ethr:0x44957e75d6ce4a5bf37aae117da86422c848f7c2", beneficiaryVASPdid: "did:ethr:0x47463999eb42dc2aaacb29624c512603221227a1", originator: encryptedOriginator, // from the Notabene PII SDK beneficiary: encryptedBeneficiary, // from the Notabene PII SDK }, }, }); ``` ## Incoming transactions Fireblocks sends Notabene the destination address, the amount, and the VASP DID assigned to the receiving vault account. If no Travel Rule message is attached, Fireblocks creates a blank one so the transaction can still be screened. There is nothing for you to create or resolve: unlike Sumsub and GTR, Notabene does not use Travel Rule messages that you manage through the API. ## Multiple legal entities To map each vault account to the right legal entity, connect your entities as a Gateway VASP with subsidiary VASPs: 1. Create the entities in Notabene. 2. Ask Notabene to establish the Gateway and subsidiary relationship. 3. Generate a shared encryption key. 4. Assign each vault account to its VASP. Each vault account maps to exactly one VASP; a VASP can cover many vault accounts. ## Testing Use Notabene's test VASPs (RoboVASPs), test environment, and Postman collection. See [Testing transactions](https://devx.notabene.id/docs/fireblocks-testing) and the [Fireblocks–Notabene Postman collection](https://www.postman.com/notabene/workspace/fireblocks-notabene-integration/overview). Some Notabene documentation is password-protected; for access, contact your Customer Success Manager. ## Limitations * **Unscreened routes:** vault-to-vault, vault-to-exchange, vault-to-Gas Station, Gas Station-to-vault, and non-custodial wallet to non-custodial wallet within the same workspace. * **Cannot run alongside Sumsub or GTR** on the same workspace. * **Assets:** Fireblocks supports a subset of the assets Notabene supports (most CoinGecko-listed assets). On Testnet: `BTC_TEST`, `ETH_TEST5`, `XRP_TEST`. To request more, contact Fireblocks Support. * Incoming transfers from Fireblocks P2P Network connections default to an "Unsupported Asset" status. * Disconnecting Notabene does not remove addresses already registered in Notabene's address book. ## On the Help Center To connect Notabene or build Travel Rule rules in the Console, see [Notabene](https://support.fireblocks.io/hc/en-us/articles/30614346879900-Notabene). # Fireblocks Object Model Source: https://developers.fireblocks.com/docs/object-model The Fireblocks platform can be viewed as consisting of the following components: ![](https://mintcdn.com/fireblocks-43c4b3ee/vGQZZADtV-e7oPsP/images/fb-object-model-diagram.png?fit=max\&auto=format\&n=vGQZZADtV-e7oPsP\&q=85\&s=adcf5e0ac7de1c899482da0806a6de7f) ### Fireblocks Workspace Each unique instance of Fireblocks is called a *workspace*. ### Vault accounts A Vault account is an easy way to separate and maintain security boundaries across assets. Vault accounts store your asset wallets on the Fireblocks platform. Any user with the appropriate permissions can create a vault account since that is not a workspace action that requires approval. To classify vault accounts, for example by role, region, or end-client segment, attach [tags](/docs/tags) and filter your vault accounts by them. Protected tags can also be referenced in Policy rules. The Vault structure for your application you're building on top of Fireblocks can either be 'Segregated' or 'Omnibus'. #### **Segregated** With this vault structure, each user of your application is assigned individual vault accounts. * For example, you may have vault accounts labeled as **User #1, User #2, User #3, etc...** each of which maps to a user of the application you are building on top of Fireblocks. #### **Omnibus** With this vault structure, all users of your application are assigned one, or several, centralized vault accounts. * For example, you may have a single vault account labeled as **Omnibus**, where all of your user's funds and assets are swept for a single withdrawal. ### Asset wallets Asset wallets are used to manage internal deposit addresses. Each asset wallet contains at least one deposit address for its asset type. Each vault account can hold any number of assets supported by Fireblocks, including multiple deposit addresses for blockchains that support it, such as UTXO-based chains like BTC. Depending on the asset type, different wallets may support multiple address types or formats. Each asset wallet is assigned to a single digital asset supported by Fireblocks. Your workspace contains multiple assets from different blockchains. You can use one of the existing assets or add a new asset to your workspace and use it as an asset wallet. [Learn how to create vault accounts and asset wallets using the Fireblocks API.](/docs/create-direct-custody-wallets) ### Connected accounts To transfer funds securely to/from exchanges, integrate a connected account by adding your API credentials for those exchanges. ### Fiat accounts To transfer fiat through one of our integrated fiat providers, you need to explicitly add your fiat provider's API credentials within your desired workspace fiat account. ### Whitelisted addresses > [!NOTE] > > In the API and Webhooks, whitelisted addresses are referred to as **unmanaged wallets**. Whitelisted addresses are an additional security measure for interacting with blockchain addresses outside your vault. Each whitelisted address is one of three wallet types: internal, external, or contract. * **Internal wallet** - a deposit address existing inside your organization. * **External wallet** - a deposit address existing outside your organization. * **Contract wallet** - a deposit address of an on-chain smart contract. > [!NOTE] > > This only applies to smart contracts on EVM-compatible blockchains. # Off Exchange Source: https://developers.fireblocks.com/docs/off-exchange Fireblocks is the digital asset management infrastructure for the leading trading desks, hedge funds, brokerages, custodians, and banks. Through the Fireblocks Console, users connect and trade on over 30 exchanges. As part of an important initiative, making sure that the end user still has complete control over his own asset Fireblocks offers the Off Exchange solution. This way ,the end user can enjoy the various benefits of the exchange while avoiding the risk of a centralized malfunction. Benefits: * Complete end user control * Provide a trustable relationship, where the customer safely continues his work with the exchange * Control rate limits and response codes * Manage the supported assets * Optimize the platform load for requests from Fireblocks [Read the Fireblocks Off-Exchange official documentation](https://fireblocks.github.io/fireblocks-off-exchange/) # Official Public Keys Use the following key(s) to validate requests received from Fireblocks on Off-Exchange: ## Development Testing ```yaml theme={"system"} -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtug0j82+C1WCcqCPEzEm sao1kH4gIDHewI06SsiB1aIdZ/v+I/PTRTYhKC0JVtsio+KeF/YDWI3c1RZrMa9/ 7yOoQ8QNNG0U65q2//LoSz3He6E9/7b5V+BrQwvCHPOv4kp+un9bZ23BqvX+jgJQ aKTvjEeBUW6fOh2oupRGH51teBcQpmgPFP1b26BGFSiGBdyB1OX6qYchIv5C/XW/ d3NoOUD96kEMEsDUKCvXwPp5gelqZQoaGZZatE5AllFdJJQKXU8DWDXTJMSyxIIY rzx6S5RToDDK/Z7SKG4k8q1pd6pViNe88DJYV6kJdSgiYThl07sATWo+6Ev6nFSo z69Jx+BWx5v2aLJfc6ghuN7j5dOBtex+tklHMw0s1XCiE14Pyi6px9dNNJApp6wM LIDmMeKTZ65ar22q5ySgrNQHvVOy66zRZhfOu5fueSPa4C3CBoarCpiUIQTVNzf5 AA3qBKfunIUqHYNzdvPt/MwQErW1G9XPxGC+az0Cc4qEmICUSlYiFC1BLD6YKpjP MsPkuj2d9MYfDUQktt1IKsWOTE6NNVzmYpbZkA7TD6hcqd7rV6Zf5TcK9vvEB1V5 F0jJYUdNXygdzKOwY/OlQ0eoF5vb6XU46y24cCnKgJ23ZfdLJJrFOdpbEaooErhn 37BWbAWH9EpcHM9pFNUYOhkCAwEAAQ== -----END PUBLIC KEY----- ``` ## Production Testing ```yaml theme={"system"} -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9JxE/VqDSCnUD5030Ufm CoTiOtbLGRGZ6XK1KNhtD1c0MQUtiHoCCcAmmyOlTuemaPjrscGFOdITeubEPoO1 +smHziM2LkzSgtOwdgTXA+pYxnMe9lDXCtgD5mRRUQB6IqsqZN8bJcG+VuF/mMZy qkuUIskrjJ+kn0ZfiR0PHVYkaUzvHhmyB8ZxrQTOWLB7H+62g7ZBI1U1cOcyHnSH HVh0TpZ573fztqQS3ECGeCnpo6jQEfBeCvwx5sBkum+3iWl9VDwXIlyjcodlYqD1 YmrYrSPJwpQlxv9zUw2LarwJU+uv3EmIRKyQdxsHO8tcXGDy6WWl/zUNw1nu6uLd kCteOJUT/SX0QlxizLFE02AAEW0hBNT0KHhD5GVN0QRkWfAJTxLSajqKEeg7M2KO 0xfh+PMEeEOzIc3GC5xv/oJ3tFt7ZawbU19WpifUcBiU2r4HLPaijxN7K9bZIfUt K5maCIATuqn1U05G0zXUvkgZ64fHBzwtMl3grp5t6++QiH3fShBD+hUQmPoTV9p3 vgPtigK2+USPyJVwa22YplY96XckkBToOcrvYZc710gpTRwFjQdJ/DfrUYqh4UaV 7k/n9sCDRM3sM353tMAmr6dFYHKN5OwviKD2t9RNMa3j67wqHAeLimFvAM8fMe5I aNSwkiUjHhlLPOqPhhXl3U8CAwEAAQ== -----END PUBLIC KEY----- ``` # OKX Travel Rule Source: https://developers.fireblocks.com/docs/okx-travel-rule The Travel Rule `piiData` fields OKX requires for withdrawals to hosted and private wallets, with an example. OKX requires Travel Rule `piiData` for withdrawals. What you send depends on whether the destination is a hosted wallet, at a VASP or exchange, or a private wallet. For how to build, encrypt, and send the payload, see [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule). ## Required fields for withdrawals | Path | Values | Required when | | - | - | - | | `data.beneficiary.isHosted` | `"true"` for exchange-hosted accounts; `"false"` for private accounts | Always | | `data.beneficiary.entityType` | `Individual`, `Business` | Always | | `data.beneficiary.names[0].primaryName` | Free text, with `nameType` `Latin` | Hosted wallet, `Individual` | | `data.beneficiary.names[0].secondaryName` | Free text | Hosted wallet, `Individual` | | `data.beneficiary.company.name` | Free text | Hosted wallet, `Business` | | `data.beneficiary.postalAddress.streetName` | Free text | Hosted wallet | | `data.beneficiary.postalAddress.buildingNumber` | Free text | Hosted wallet | | `data.beneficiary.postalAddress.city` | Free text | Hosted wallet | | `data.beneficiary.postalAddress.postalCode` | Free text | Hosted wallet | | `data.beneficiary.postalAddress.subdivision` | Free text | Hosted wallet | | `data.beneficiary.postalAddress.country` | Free text | Hosted wallet | | `data.beneficiaryVASP.vaspCode` | Value from the OKX VASP list | Hosted wallet | | `data.beneficiaryVASP.vaspName` | Free text | Hosted wallet and the VASP is not in the OKX VASP list | ## Identify the counterparty VASP For `beneficiaryVASP`, use the VASP's DID as `vaspCode`. For a VASP without a DID, set `vaspCode` to `"-2"` and `vaspName` to the VASP's name. OKX publishes the full list of VASPs it supports through its [exchange list endpoint](https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-exchange-list-public). ## Example ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": { "isHosted": "true", "entityType": "Individual", "names": [{ "primaryName": "John", "secondaryName": "Doe", "nameType": "Latin" }], "postalAddress": { "streetName": "Oak", "buildingNumber": "1", "city": "Boston", "postalCode": "11000", "subdivision": "Massachusetts", "country": "US" } }, "beneficiaryVASP": { "vaspCode": "-2", "vaspName": "ACME VASP" } } } ``` ## On the Help Center To provide OKX Travel Rule data in the Console, see [OKX travel rule](https://support.fireblocks.io/hc/en-us/articles/30750129326364-OKX-travel-rule). # On/Off-Ramps Source: https://developers.fireblocks.com/docs/on-off-ramps-travel-rule Add originator and beneficiary information to on-ramp and off-ramp orders through the Fireblocks Trading API. On-ramps convert fiat to crypto; off-ramps convert crypto to fiat. When you place these orders through a connected account with a provider such as Alfred Pay, Yellow Card, or Scrypt, the provider may require information about the originator and beneficiary, which you include in the order's `participantsIdentification` object. It does not go through a Travel Rule provider or your Travel Rule policy; the provider applies its own requirements, which depend on the provider, the payment rail, and who the funds belong to. ## What you can do through the API | Task | Console | API | | - | - | - | | Read a provider's requirements | No | [`GET /trading/providers`](/api-reference/trading/get-providers) | | Create an order with participant information | No | `POST /trading/orders` (`fireblocks.tradingBeta.createOrder`) | | Track an order | No | `GET /trading/orders/{orderId}` (`fireblocks.tradingBeta.getOrder`) | | Connect the provider account | Yes | No | ## Step 1: Before you start * **Trading API access.** On/off-ramp orders use the [Trading API](/docs/trading-api-overview), which is in Beta. Contact your Customer Success Manager for access. * **A connected account with the provider**, set up in the Console. See [Connected accounts and balances](https://support.fireblocks.io/hc/en-us/articles/27221452097692-Connected-accounts-and-balances) on the Help Center. ## Step 2: Build participantsIdentification Set the relationship separately for the originator and the beneficiary. For example, you can fund an on-ramp yourself (`FirstParty` originator) and pay out to a customer (`ThirdParty` beneficiary). | Relationship | Meaning | Data required | | - | - | - | | `FirstParty` | You're acting for yourself; the provider already holds your KYC. | Minimal: the relationship type and entity type. | | `ThirdParty` | You're acting for someone else. | The provider may require full PII, such as name, address, and date of birth. | | Field | Description | | - | - | | `originator`, `beneficiary` | One object per party. | | `participantRelationshipType` | `FirstParty` or `ThirdParty`. | | `entityType` | `INDIVIDUAL` or `BUSINESS`. | | `fullName` | `firstName` and `lastName`, for individuals. | | `businessName` | The business's legal name, for businesses. | | `registrationNumber` | The business's registration number, for businesses. | | `dateOfBirth` | ISO date, for example `1985-03-15`. | | `postalAddress` | `streetName`, `buildingNumber`, `postalCode`, `city`, `subdivision`, `district`, and `country` (ISO alpha-2). | | `externalReferenceId` | Your own reference for the party. | Acting for yourself: ```json theme={"system"} { "participantsIdentification": { "originator": { "entityType": "INDIVIDUAL", "participantRelationshipType": "FirstParty" }, "beneficiary": { "entityType": "INDIVIDUAL", "participantRelationshipType": "FirstParty" } } } ``` Paying out to a third party: ```json theme={"system"} { "participantsIdentification": { "originator": { "entityType": "BUSINESS", "participantRelationshipType": "FirstParty" }, "beneficiary": { "entityType": "INDIVIDUAL", "participantRelationshipType": "ThirdParty", "fullName": { "firstName": "Alexander", "lastName": "Johnson" }, "dateOfBirth": "1985-03-15", "postalAddress": { "streetName": "Fifth Avenue", "buildingNumber": "350", "postalCode": "10118", "city": "New York", "subdivision": "NY", "district": "Manhattan", "country": "US" }, "externalReferenceId": "person_ref_7f3e2d1c4b8a5e9f" } } } ``` ## Step 3: Create the order ```ts theme={"system"} const { data: order } = await fireblocks.tradingBeta.createOrder({ createOrderRequest: { via: { type: "PROVIDER_ACCOUNT", providerId: "YELLOWCARD", accountId }, executionRequestDetails: { type: "MARKET", side: "BUY", baseAmount: "250", baseAssetId: "XOF", baseAssetRail: "LBT", quoteAssetId: "USDT_ERC20", quoteAssetRail: "BLOCKCHAIN", }, settlement: { type: "DVP", sourceAccount: { type: "VAULT_ACCOUNT", accountId: vaultAccountId }, destinationAccount: { type: "UNMANAGED_WALLET", accountId: whitelistedBankAccountId }, }, participantsIdentification: { originator: { entityType: "INDIVIDUAL", participantRelationshipType: "FirstParty" }, beneficiary: { entityType: "INDIVIDUAL", participantRelationshipType: "FirstParty" }, }, }, idempotencyKey: `order-${Date.now()}`, }); ``` When the source is a vault account, Fireblocks creates a transfer to the provider, approved and signed under your workspace's Policies like any other transfer, before the order executes. When tracking the order, `PENDING_USER_ACTION` means it needs action from you, such as an approval; `COMPLETED`, `FAILED`, and `CANCELED` are terminal. For quotes, settlement types, and the full order flow, see [On/Off-Ramp, and cross-chain Swap via CeFi](/docs/on-ramp-off-ramp-and-bridgeswap-via-account-based-providers-cefi). ## Whitelisted destinations Register external bank accounts and crypto addresses as whitelisted destinations, and use them as the order's `destinationAccount` with type `UNMANAGED_WALLET`. They are verified once and reused for future orders. ## Provider-specific requirements Some providers publish their requirements in their provider manifest. Providers can also ask for more information after you create an order, through the order's requirements (RFI) flow; Circle Payments Network (CPN) supports this. See [Off-ramp with Circle Payment Network (CPN)](/docs/circle-payments-network-cpn-api-guide). ## Limitations * **Placing orders is API only**, through the Trading API in Beta. * **Required fields vary by provider and rail;** there is no single field set that works for every provider. ## On the Help Center For an overview of on/off-ramps and connected accounts, see [On/Off-Ramps Travel Rule Overview](https://support.fireblocks.io/hc/en-us/articles/30614392673564-On-Off-Ramps-Travel-Rule-Overview). # On/Off-Ramp, and cross-chain Swap via CeFi Source: https://developers.fireblocks.com/docs/on-ramp-off-ramp-and-bridgeswap-via-account-based-providers-cefi This guide provides step-by-step examples of how to execute various order operations using the Fireblocks Trading API via account-based providers. These require you to connect an account before trading. For instructions on how to connect accounts, see [Fireblocks Connected Accounts](https://support.fireblocks.io/hc/en-us/articles/27221452097692-Connected-accounts-and-balances). This guide covers multiple use cases including on-ramp (fiat to crypto), off-ramp (crypto to fiat), and bridging (crypto-to-crypto). ## Prerequisites Before you begin, ensure you have completed the prerequisites outlined in the [Trading API Overview](/docs/trading-api-overview): * ✅ Fireblocks API credentials configured * ✅ Fireblocks TypeScript SDK installed (`@fireblocks/version 13.x or later`) * ✅ Connected account(s) set up with your chosen provider(s) via the Fireblocks Console (see [Fireblocks Connected Accounts](https://support.fireblocks.io/hc/en-us/articles/27221452097692-Connected-accounts-and-balances) for setup instructions) ## SDK Setup First, set up your Fireblocks SDK client: ```typescript TypeScript theme={"system"} import { Fireblocks } from '@fireblocks/ts-sdk'; import * as fs from 'fs'; // Initialize the Fireblocks SDK const fireblocks = new Fireblocks({ apiKey: 'your-api-key', secretKey: fs.readFileSync('/path/to/your/secret.key', 'utf8'), basePath: 'https://api.fireblocks.io', }); ``` ## Provider and account discovery Before trading with account-based providers, find out which providers are available and which accounts you have connected. If you haven't connected any accounts yet, see [Fireblocks Connected Accounts](https://support.fireblocks.io/hc/en-us/articles/27221452097692-Connected-accounts-and-balances) for setup instructions. ### Step 1: Get Providers and Connected Accounts Retrieve all trading providers. For account-based providers, the response includes provider information and connected accounts, allowing you to extract both `providerId` and `accountId` in a single call. ```typescript TypeScript theme={"system"} // Get all trading providers const response = await fireblocks.trading.getTradingProviders({ pageSize: 20 }); // Filter for account-based providers const accountBasedProviders = response.data.data.filter(p => p.accountBased); // Access provider and account information for (const provider of accountBasedProviders) { console.log(`Provider: ${provider.id} (${provider.name})`); provider.accounts?.forEach(account => { console.log(` Account: ${account.id} (${account.name})`); }); } ``` ### Response Example ```json JSON theme={"system"} { "data": [ { "id": "ALFREDPAY", "name": "Alfred Pay", "accountBased": true, "connected": true, "accounts": [ { "id": "acc_5e9a2d1c4b7f3e8a", "name": "My Alfred Pay Account" } ], "manifest": { "assetTypes": ["FIAT", "DIGITAL"], "capabilities": ["TRADING"] } }, { "id": "YELLOWCARD", "name": "Yellow Card", "accountBased": true, "connected": true, "accounts": [ { "id": "acc_9f4e2d8b1c6a5e73", "name": "Yellow Card Main Account" } ], "manifest": { "assetTypes": ["FIAT", "DIGITAL"], "capabilities": ["TRADING"] } } ], "total": 2, "next": null } ``` ### Key Fields Explained | Field | Description | | - | - | | `id` | Unique identifier for the provider (use as `providerId` in subsequent API calls) | | `name` | Display name of the provider | | `accountBased` | `true` for account-based providers, `false` for direct access providers | | `connected` | Whether you have at least one connected account with this provider | | `accounts` | Array of connected accounts for this provider. Each account has an `id` (use as `accountId` in subsequent API calls) and `name` | For more details about this endpoint, see the [Get Providers API Reference](/api-reference/trading/get-providers). ### Step 2: Get Supported Trading Pairs For each connected account, discover which trading pairs are supported: ```typescript TypeScript theme={"system"} // Get supported trading pairs for a specific connected account const response = await fireblocks.trading.getTradingPairs({ accountId: 'acc_9f4e2d8b1c6a5e73', }); // Response data const tradingPairs = response.data; // tradingPairs = [ // { // "id": "7c9e8f2a-4b5d-4e1c-9a3b-6f8d2e5c7a1b", // "toAsset": "USDC", // "fromAsset": "USD", // "prefunded": false, // "fromAssetRail": "ACH" // } // ] ``` ### Step 3: Get Indicative Rates Get indicative rates for price preview (these are not executable quotes): ```typescript TypeScript theme={"system"} // Get indicative rates const response = await fireblocks.trading.getRates({ accountId: 'acc_9f4e2d8b1c6a5e73', baseAssetId: 'USD', quoteAssetId: 'BTC', }); const rates = response.data; ``` > [!NOTE] > > Rates provide indicative prices for preview, while quotes return committed rates that can be executed until expiration. ## Clarification on how to translating user intent into baseAssetId, quoteAssetId, side, and baseAmount When creating a **Quote** or an **Order**, you express the trade using: * `baseAssetId`: the asset you **receive** on **BUY** / **give** on **SELL** * `quoteAssetId`: the counter asset used to **pay**/**receive** * `side`: * `BUY`: receive base / pay quote * `SELL`: give base / receive quote * `baseAmount`: amount in `baseAssetId` * `BUY`: base amount to receive * `SELL`: base amount to sell ### Examples | Intent | baseAssetId | quoteAssetId | side | baseAmount | | - | - | - | - | - | | Buying 100 BTC with USD | BTC | USD | BUY | 100 | | Buying 100 USD with BTC | USD | BTC | BUY | 100 | | Selling 100 BTC for USD | BTC | USD | SELL | 100 | | Selling 100 USD for BTC | USD | BTC | SELL | 100 | ## Participants & compliance Participant details vary by provider and rail. The `participantsIdentification` object defines the originator and beneficiary involved in an order. ### Example structure ```json theme={"system"} { "participantsIdentification": { "originator": { "externalReferenceId": "user_123", "participantRelationshipType": "FirstParty", "entityType": "INDIVIDUAL" }, "beneficiary": { "participantRelationshipType": "ThirdParty", "fullName": { "firstName": "Alice", "lastName": "Lee" } } } } ``` ### Key concepts * **First-party**: Acting for self — provider already holds KYC; minimal data required. * **Third-party**: Acting for another — provider may require full PII. The level of PII data required depends on the provider and the relationship type (first-party vs. third-party). ## Use Case Examples The following sections demonstrate different trading use cases with account-based providers: ## Use Case 1: On-Ramp - DVP Settlement with Market Execution **Use Case:** Convert fiat to crypto at the market rate. The user creates the order, receives payment instructions from the provider, deposits the funds, and once payment is confirmed, the provider executes and settles the crypto, ensuring delivery versus payment (DVP). **Settlement Type:** DVP (Delivery vs Payment)\ **Execution Type:** Market ### **Note:** The `baseAssetId`, `baseAssetRail`, `quoteAssetId`, and `quoteAssetRail` values used in the order must match a supported trading pair returned from the `getTradingPairs` endpoint (see Step 2 in Getting Started). This ensures the provider supports the specific asset and rail combination you're requesting. ### Step 1: Create the Order ```typescript theme={"system"} // Create on-ramp order with DVP settlement and market execution const response = await fireblocks.trading.createOrder({ createOrderRequest: { via: { type: 'PROVIDER_ACCOUNT', providerId: 'bridge-provider-001', accountId: 'acc_9f4e2d8b1c6a5e73', }, executionRequestDetails: { type: 'MARKET', side: 'BUY', baseAmount: '1000', baseAssetId: 'USD', baseAssetRail: 'ACH', quoteAssetId: 'USDC', quoteAssetRail: 'BLOCKCHAIN', }, settlement: { type: 'DVP', sourceAccount: { type: 'EXTERNAL', }, destinationAccount: { type: 'ONE_TIME_ADDRESS', address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', }, }, participantsIdentification: { originator: { entityType: 'BUSINESS', participantRelationshipType: 'FirstParty', }, beneficiary: { entityType: 'INDIVIDUAL', participantRelationshipType: 'ThirdParty', fullName: { lastName: 'Johnson', firstName: 'Alexander', }, postalAddress: { streetName: 'Fifth Avenue', buildingNumber: '350', postalCode: '10118', city: 'New York', subdivision: 'NY', district: 'Manhattan', country: 'US', }, externalReferenceId: 'person_ref_7f3e2d1c4b8a5e9f', dateOfBirth: '1985-03-15', }, }, customerInternalReferenceId: '32e423', }, idempotencyKey: `order-${Date.now()}`, }); const order = response.data; ``` ### Step 2: Handle Payment Instructions Since the settlement type is DVP and the source type is EXTERNAL, the funds will move from outside Fireblocks. The order creation response will include payment instructions for the rail which you select (e.g., ACH, SEPA, Wire): ```json theme={"system"} { "paymentInstructions": { "referenceId": "REF-ACH-001", "type": "ACH", "address": { "accountHolder": { "name": "Bridge Provider Ltd.", "address": "123 Main Street", "city": "New York", "country": "US", "subdivision": "NY", "postalCode": "10001" }, "bankName": "JPMorgan Chase Bank N.A.", "bankAccountNumber": "9876543210", "routingNumber": "021000021", "accountType": "CHECKING" } } } ``` ### Considerations * For third-party payouts, like the one in the example, the provider must receive the beneficiary details (PII as required by the provider). In this case, you are the originator — acting as a first party, already KYC'd with the provider. Therefore, the only data needed is to set the `beneficiary.participantRelationshipType = "FirstParty"`. * Since the settlement type is DVP and the source type is EXTERNAL, the funds will move from outside Fireblocks. As a result, the order creation response will include payment instructions for the rail which you select (e.g., ACH, SEPA, Wire). ## Use Case 2: On-Ramp - Prefunded settlement with quote execution **Use Case:** Convert fiat to crypto at a locked rate from a committed quote. Funds are already held in the account; no payment instructions are returned in the response, and the provider will execute and settle the request immediately. **Settlement Type:** Prefunded\ **Execution Type:** Quote ### Step 1: Create a Quote ```typescript theme={"system"} // Create a quote for on-ramp const quoteResponse = await fireblocks.trading.createQuote({ createQuote: { scope: [ { providerId: 'SCRYPT', accountId: 'scrypt_acc_5e9a2d1c4b7f3e8a', }, ], side: 'BUY', baseAssetId: 'USDT_ERC20', quoteAssetId: 'TRX_USDT_S2UZ', baseAmount: '15000', }, idempotencyKey: `quote-${Date.now()}`, }); const quote = quoteResponse.data.quotes[0]; ``` In the quote response, you will receive all related metadata, including the quote ID (`id`) and expiration time (`expiresAt`). The quote ID is then used in the next step to create an order and execute the trade. ### Step 2: Execute order with quote ```typescript theme={"system"} // Execute order using the quote from Step 1 const response = await fireblocks.trading.createOrder({ createOrderRequest: { via: { type: 'PROVIDER_ACCOUNT', accountId: 'acc_5e9a2d1c4b7f3e8a', providerId: 'ALFREDPAY', }, executionRequestDetails: { type: 'QUOTE', reQuote: { type: 'MARKET', }, quoteId: 'quote_8f2e4d1a9c5b7e3f', }, settlement: { type: 'PREFUNDED', destinationAccount: { type: 'VAULT_ACCOUNT', accountId: 'vault_acc_9f3e2d1c4b8a7e5f', }, }, participantsIdentification: { originator: { entityType: 'INDIVIDUAL', participantRelationshipType: 'FirstParty', }, beneficiary: { entityType: 'INDIVIDUAL', participantRelationshipType: 'FirstParty', }, }, }, idempotencyKey: `order-${Date.now()}`, }); const order = response.data; ``` ### Considerations * In this example, you perform an on-ramp for yourself, not for a third-party recipient. Therefore, both the originator and beneficiary use `"participantRelationshipType": "FirstParty"`, and no additional PII data is required. * The `quoteId` used in the order request is the same ID returned in the response from the `POST /trading/quotes` call. ## Use Case 3: Off-Ramp - DVP settlement with market execution **Use Case:** Convert crypto to fiat at the market rate. The provider waits for confirmation of the crypto deposits before finalizing the settlement, ensuring delivery versus payment (DVP). **Settlement Type:** DVP\ **Execution Type:** Market ### **Note:** The `baseAssetId`, `baseAssetRail`, `quoteAssetId`, and `quoteAssetRail` values used in the order must match a supported trading pair returned from the `getTradingPairs` endpoint (see Step 2 in Getting Started). This ensures the provider supports the specific asset and rail combination you're requesting. ### Step 1: Create the Order ```typescript theme={"system"} // Create off-ramp order with DVP settlement and market execution const response = await fireblocks.trading.createOrder({ createOrderRequest: { via: { type: 'PROVIDER_ACCOUNT', providerId: 'YELLOWCARD', accountId: 'acc_9f4e2d8b1c6a5e73', }, executionRequestDetails: { type: 'MARKET', side: 'BUY', baseAmount: '250', baseAssetId: 'XOF', baseAssetRail: 'LOCAL_BANK_TRANSFER_AFRICA', quoteAssetId: 'USDT_ERC20', quoteAssetRail: 'BLOCKCHAIN', }, settlement: { type: 'DVP', sourceAccount: { type: 'VAULT_ACCOUNT', accountId: 'vault_acc_5e9a2d1c4b7f3e8a', }, destinationAccount: { type: 'EXTERNAL_WALLET', accountId: 'fiat_whitelisted_acc_9f3e2d1c4b8a7e5f', }, }, participantsIdentification: { originator: { entityType: 'INDIVIDUAL', participantRelationshipType: 'FirstParty', }, beneficiary: { entityType: 'INDIVIDUAL', participantRelationshipType: 'FirstParty', }, }, }, idempotencyKey: `order-${Date.now()}`, }); const order = response.data; ``` ### Considerations * In this example, you perform an off-ramp for yourself, not for a third-party recipient. Therefore, both the originator and beneficiary use `"participantRelationshipType": "FirstParty"`, and no additional PII data is required. * In this example, you chose to use a whitelisted account as the destination fiat account type. This allows you to register and manage external bank accounts (and also crypto addresses) directly within Fireblocks, ensuring secure, verified destinations that can be safely reused for future orders and transactions. * Since the source of this order is a Vault Account, once the provider receives the order request, a transfer request will be automatically created from your vault to the provider. You will then need to approve and sign this transfer in accordance with the workspace's policy rules before the order can be executed. ## Use Case 4: Bridging (Crypto-to-Crypto) - DVP settlement with quote execution **Use Case:** Execute a cross-chain bridging operation through the provider's connected accounts. In this flow, the user swaps USDT on Ethereum for USDT on Tron using their Scrypt provider account. **Settlement Type:** DVP\ **Execution Type:** Quote ### Step 1: Create a Quote ```typescript theme={"system"} // Create a quote for crypto-to-crypto bridging const quoteResponse = await fireblocks.trading.createQuote({ createQuote: { scope: [ { providerId: 'SCRYPT', accountId: 'scrypt_acc_5e9a2d1c4b7f3e8a', }, ], side: 'BUY', baseAssetId: 'USDT_ERC20', quoteAssetId: 'TRX_USDT_S2UZ', baseAmount: '15000', }, idempotencyKey: `quote-${Date.now()}`, }); const quote = quoteResponse.data.quotes[0]; ``` In the quote response, you will receive all related metadata, including the quote ID (`id`) and expiration time (`expiresAt`). The quote ID is then used in the next step to create an order and execute the trade. ### Step 2: Execute Order with Quote ```typescript theme={"system"} // Execute bridging order using the quote from Step 1 const response = await fireblocks.trading.createOrder({ createOrderRequest: { via: { type: 'PROVIDER_ACCOUNT', providerId: 'SCRYPT', accountId: 'scrypt_acc_5e9a2d1c4b7f3e8a', }, executionRequestDetails: { type: 'QUOTE', reQuote: { type: 'MARKET', }, quoteId: 'quote_khge4d1a9c5bmvby', }, settlement: { type: 'DVP', sourceAccount: { type: 'VAULT_ACCOUNT', accountId: 'vault_acc_5e9a2d1c4b7f3e8a', }, destinationAccount: { type: 'VAULT_ACCOUNT', accountId: 'vault_acc_5e9a2d1c4b7f3e8a', }, }, participantsIdentification: { originator: { entityType: 'INDIVIDUAL', participantRelationshipType: 'FirstParty', }, beneficiary: { entityType: 'INDIVIDUAL', participantRelationshipType: 'FirstParty', }, }, }, idempotencyKey: `order-${Date.now()}`, }); const order = response.data; ``` ### Considerations * In this example, you perform bridging via a connected account for yourself, not for a third-party recipient. Therefore, both the originator and beneficiary use `"participantRelationshipType": "FirstParty"`, and no additional PII data is required. * The `quoteId` used in the order request is the same ID returned in the response from the `POST /trading/quotes`call. * Since the source of this order is a Vault Account, once the provider receives the order request, a transfer will automatically be initiated from your Vault Account to the provider's connected account on Scrypt. This transfer is governed by the workspace's Transfer Policy, just like any other transfer in Fireblocks. * **ReQuote Option:** Instead of `"type": "MARKET"`, you can use `"type": "RETRY"` with `count` (1-10 retry attempts) and optional `slippageBps` (slippage tolerance in basis points) to automatically retry quote generation if the original quote expires, giving you more control over price execution. ## Order Tracking & Monitoring After creating an order, monitor its execution status to track completion. ### Polling for order status ```typescript theme={"system"} // Get order status and details const response = await fireblocks.trading.getOrder({ orderId: 'order_id_here', }); const order = response.data; // Check: order.status, order.executionSteps, order.createdAt, etc. ``` ### Order Status Values | Status | Description | | - | - | | `CREATED` | Order has been created and is being processed | | `AWAITING_PAYMENT` | Waiting for payment to be received | | `PENDING_USER_ACTION` | Requires user action (e.g., approval in console) | | `PROCESSING` | Order is being executed | | `COMPLETED` | Order has been successfully completed | | `FAILED` | Order execution failed | | `CANCELED` | Order was canceled | ### **Note:** FAILED, COMPLETED and CANCELED are terminal states. ### Webhooks Subscribe to order-update events through the Fireblocks Webhooks API. See the [Fireblocks Webhooks API documentation](/) for more details. ## Additional Resources * [Trading API Overview](/docs/trading-api-overview) * [Bridging via Direct Access Providers (DeFi)](/docs/swap-via-direct-access-providers-defi) - For crypto-to-crypto bridging using direct access providers like Uniswap * Fireblocks SDKs: [Python](https://pypi.org/project/fireblocks/) | [TypeScript](https://www.npmjs.com/package/@fireblocks/ts-sdk) | [Java](https://central.sonatype.com/artifact/com.fireblocks.sdk/fireblocks-sdk/overview) * [Fireblocks Developer Portal](/) ## Support For questions or issues with the Trading API: * Contact your Fireblocks [Customer Success Manager](https://support.fireblocks.io/hc/en-us/requests/new?ticket_form_id=6947882197532) * Email: [CSM@fireblocks.com](mailto:CSM@fireblocks.com) * Check the [Fireblocks Developer Portal](/) for updates # Overview Source: https://developers.fireblocks.com/docs/overview > [!NOTE] > > **Unsure about which custody model is right for you?** Read our “[Guide to Digital Asset Wallets and Service Providers](https://www.fireblocks.com/a-guide-to-digital-asset-wallets-and-service-providers)” for insights into evaluating digital asset wallet and service providers for your business Fireblocks provides customers with an option to choose what custody model works best for their use case. In this guide we will outline the main technical and conceptual differences between Embedded Wallets and Direct Custody wallets. Following are the main differences between direct custody and Embedded Wallets wallets in Fireblocks. 1. Direct custody wallets use a 3-of-3 multi-party computation (MPC) signature scheme while Embedded Wallets wallets use a 2-of-2 signature scheme. 2. There is only one master key per workspace for direct custody wallets, while each Embedded Wallets wallet has its own master key. 3. UTXO-based assets, such as BTC, can have multiple deposit addresses per wallet per vault account for direct custody wallets. For Embedded Wallets wallets, one wallet can have multiple accounts while each account can hold only 1 BTC address. 4. The Fireblocks Network and exchange integrations are supported for direct custody wallets only. # Direct Custody Wallets ## Structure ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/d76e1cc-Custodial_Wallets_1.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=f7bc3c8ee6468bf22c2a2759ea281ccc) * The main section of the workspace is the Fireblocks Vault which holds all MPC wallets. * Inside the Vault, an unlimited number of vault accounts can be created. This allows to segregate between distinct clients and various use cases. * Each vault account can hold as many asset wallets as you need. However, you can only have one wallet per asset. * In a vault account, asset wallets can accommodate numerous deposit addresses for UTXO-based assets, while account-based assets are assigned with a single address. ## Secret key management and wallet derivation ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/01ed368-Secret_key_management_and_wallet_derivation_for_the_custodial_wallet_structure_.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=68cd81fe9f814b13e7df0be42e3ba7c9) Each vault account is identified by its *vault account ID*. This identifier is used for derivation of the asset wallets in a specific vault account. Using one master key (split into three key shards) for the entire workspace, you can create an unlimited number of vault accounts. The vault account ID acts as the account value of the derivation path. Each asset within this specific Vault would be derived according to the following structure: m/purpose/coinType/account/change/index. * **m**: master private key * **purpose**: the derivation standard (BIP44 in our example) * **coinType**: the unique identifier of an asset (0 for BTC, 60 for ETH, etc.) * **account**: the vault account ID * **change**: always 0 * **index**: the address index (always 0 except for UTXO based assets such as Bitcoin) # Embedded Wallets ## Structure ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/c593861-Fireblocks_EW_structure.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=627e08fefd9e46bf94af9dbc5d4d177f) Fireblocks Embedded Wallets wallets can be used in parallel with direct custody wallets. One workspace can support both structures. However, Fireblocks strongly recommends splitting the direct custody and the Embedded Wallets parts of your business into two different workspaces due to some settings that are shared by both types of wallets in the same workspace. Fireblocks Embedded Wallets wallets use 2-of-2 MPC signature scheme. One key share is stored within an Intel SGX-enabled server managed by Fireblocks, and the second key share is stored on the end user's device. ## Secret key management and wallet derivation ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/63e0ccb-Secret_key_management_and_wallet_derivation_for_the_Non_Custodial_Wallet_structure.png?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=0aab441935557ab464631fa87f184c42) The derivation for Embedded Wallets wallets is almost the exact same as the derivation for direct custody wallets. The main difference is that each Embedded Wallets wallet will have its own master key, which is split into two key shards. In a single Embedded Wallets wallet, you can create an unlimited number of accounts while each account can have only one supported asset per asset type. # Define Payment Flows Source: https://developers.fireblocks.com/docs/payment-flows-copy # What are Payment Flows? Fireblocks empowers you to leverage blockchains and digital rails for efficient, dependable, and predictable payments. The Payments feature in Fireblocks enables the creation of secure, dynamic payment flows that consist of various actions within a single solution. Payment flows allow you to streamline and automate your payment transactions. By configuring and executing payments directly from the Payments page in the Fireblocks Console or via the [Fireblocks API](/reference/createflowconfiguration) , you can manage various types of financial operations with ease and precision. # How does it work? The Payments API involves two resources. * **Workflow Configuration (WC):** This resource acts as a template for your payment flow. It contains the types of operations and each operation’s schema of parameters. Each operation under a WC is referred to as a workflow configuration operation (WCO). * **Workflow Execution (WE):** This resource allows you to validate, preview, and launch your payment flow based on a specific WC. Each operation within a WE is referred to as a workflow execution operation (WEO). Generally, the payment workflow consists of the following three steps: * [Create a WC](/reference/createflowconfiguration). * The WC completes the validation process and moves to `READY_FOR_EXECUTION` state. * [Create a WE based on the WC](/reference/createflowexecution). * The WE completes the validation process and the preview process and moves to `READY_FOR_LAUNCH` state. * [Launch the WE](/reference/launchflowexecution). The validation and preview processes are not instantaneous. Before continuing to the next step, the WC or WE should be in a ready state. The validation process is required again when creating the WE because: * Changes may have occurred during the time between creating a WC and creating a WE. For example, the account could have been removed from the workspace. * New parameters may have been added when creating the WE and should be verified. # Overview Source: https://developers.fireblocks.com/docs/perform-drs-process Fireblocks provides a comprehensive backup and recovery solution to ensure that you always have access to your assets, even if you lose access to your signing devices or in the unlikely event that Fireblocks suspends operations. Your Fireblocks Vault is a secure multi-party computation (MPC) based wallet that prevents your private key from being a single point of failure when you sign transactions. With Fireblocks MPC, one key share is stored on your company’s hardware, either in each user with signing privileges’ Fireblocks mobile app or on a server that hosts your API Co-Signer. You have two corresponding key shares, which Fireblocks stores at top-tier cloud providers. Your three key shares are never together in one place to expose your full private key. # Recovery Tools Fireblocks recommends using our [native self-serve recovery tool](https://support.fireblocks.io/hc/en-us/articles/12023480536732-Introduction-to-in-house-key-backup-and-recovery). Alternatively, you can use [one of our DRS partners](https://support.fireblocks.io/hc/en-us/articles/17211307086364-Third-Party-Disaster-Recovery-Services). # Getting Started Source: https://developers.fireblocks.com/docs/quickstart Install the Fireblocks Documentation MCP, then use an SDK or the Fireblocks CLI to make your first Fireblocks API calls. By the end of this guide, you will have: * Connected your agent to the Fireblocks docs * Created and approved an API user * Initialized the Fireblocks SDK or CLI * Created a vault account * Sent your first transaction ## Step 0: Connect your AI agent to Fireblocks docs Before you start, install the **Fireblocks Documentation MCP** so your agent (Cursor, Claude Code, Codex, or other) can access docs in real time to ensure up to date, accurate implementation context. **Cursor** [Click here to add the Fireblocks Documentation MCP to Cursor.](https://cursor.com/en/install-mcp?name=fireblocks-docs\&config=eyJ1cmwiOiJodHRwczovL2RldmVsb3BlcnMuZmlyZWJsb2Nrcy5jb20vbWNwIn0=) > [!TIP] > > If the install link doesn't open in your environment, open Cursor's MCP installer manually and point it at `https://developers.fireblocks.com/mcp`. **Claude Code** ```bash theme={"system"} claude mcp add --transport http fireblocks-docs https://developers.fireblocks.com/mcp ``` **Codex** ```bash theme={"system"} codex mcp add fireblocks-docs --url https://developers.fireblocks.com/mcp ``` **Other** Use the Fireblocks Documentation MCP endpoint: `https://developers.fireblocks.com/mcp` ## Step 1: Set up API authentication Fireblocks authenticates API requests with **JWT signatures**. Each request is signed with an **API secret key** (RSA private key) that stays in your environment. Fireblocks stores only the matching **public key**, which you register when you create the API user by uploading a **Certificate Signing Request (CSR)**. ### How signing works | Component | Location | Purpose | | - | - | - | | **API secret key** (private key) | Your environment only | Signs every API request to prove it came from you | | **Public key** | Fireblocks | Verifies that requests were signed by your secret key | Your API secret **never** leaves your environment. Fireblocks only receives your public key. We cannot sign requests on your behalf, and a compromised Fireblocks system would not expose your credentials. ### Generate a CSR and secret key You will create: * A **secret key** file that stays in your environment and signs requests * A **CSR** file containing the corresponding public key, which you upload in the Console 1. (Windows users) Install **Win32OpenSSL** using the default settings. 2. (Windows users) Open OpenSSL Command Prompt. 3. In your CLI, run: ```bash Shell theme={"system"} openssl req -new -newkey rsa:4096 -nodes -keyout fireblocks_secret.key -out fireblocks.csr -subj '/O=' ``` This command creates: * **fireblocks\_secret.key** — your RSA 4096 private key (API secret) * **fireblocks.csr** — the CSR you upload when creating the API user Both files are written to the current working directory. Replace `` with any string that identifies your organization (for example, `/O=Acme Corp`). The value is only used as the CSR's Organization field and does not need to match anything in the Fireblocks Console. 4. Store the secret key file securely. ### Create an API user (API key) > [!NOTE] > > If you’re using a Sandbox environment, you can skip this subsection. Sandboxes come with an API user already created and access to the [Communal Test Co-signer](/cosigners/overview/use-communal-cosigner). To create an API key with a signing role: 1. In the Fireblocks Console, go to **Developer Center** > **API Users**. 2. Select **Add API user**. 3. Name the API user, select the appropriate workspace role for it (e.g., Signer if you want it to sign transactions), and upload the `fireblocks.csr` file you generated above. 4. Select **Add user**. After the request to add the API user is approved, it will appear in the API users list. You'll receive an **API Key** (API User ID), a unique identifier for the API user that you'll use alongside your secret key. ## Choose your path: SDK or CLI From **Step 2** onward, the guide splits into two tracks that share the same API credentials. Pick the one that matches what you're building: * **SDK** — for application code (apps, services, backends) where you ship typed, maintainable Fireblocks integrations. **Recommended for production.** * **Fireblocks CLI** — for fast workspace operations, exploration, scripts, CI, and operator/agent workflows. Lets your AI agent propose exact commands you can review and run. Steps **3 through 5** each use two synced tabs. Pick **SDK** or **CLI** once and the choice carries through the rest of the guide. The SDK and CLI are not mutually exclusive; you can use both in the same workspace. ## Step 2: Initialize the SDK or CLI Pick the tab that matches your track. The same choice will apply to Steps 3–5. **SDK** Fireblocks provides official SDKs for multiple languages: * [TypeScript SDK](https://github.com/fireblocks/ts-sdk) * [Python SDK](https://github.com/fireblocks/py-sdk) * [Java SDK](/reference/java-sdk) Install the appropriate SDK for your environment: ```bash npm theme={"system"} npm install @fireblocks/ts-sdk ``` ```bash pip theme={"system"} pip install fireblocks-sdk ``` ```xml Maven theme={"system"} com.fireblocks.sdk fireblocks-sdk 33.0.0 ``` ```groovy Gradle theme={"system"} implementation 'com.fireblocks.sdk:fireblocks-sdk:33.0.0' ``` After installing the SDK, initialize the client with: * Your **API Key** (obtained from the Fireblocks Console after creating the API user) * Your **API secret key** (the `fireblocks_secret.key` file from Step 1) The SDK uses your secret key to sign each API request automatically. ```typescript TypeScript theme={"system"} import { readFileSync } from "fs"; import { Fireblocks, BasePath, TransferPeerPathType } from "@fireblocks/ts-sdk"; const fireblocks = new Fireblocks({ apiKey: "", secretKey: readFileSync("fireblocks_secret.key", "utf8"), basePath: BasePath.Sandbox, // or BasePath.US, BasePath.EU, BasePath.EU2 }); ``` ```python Python theme={"system"} from fireblocks_sdk import FireblocksSDK api_key = "" with open("fireblocks_secret.key", "r") as f: private_key = f.read() fireblocks = FireblocksSDK(private_key, api_key) ``` ```java Java theme={"system"} import com.fireblocks.sdk.BasePath; import com.fireblocks.sdk.ConfigurationOptions; import com.fireblocks.sdk.Fireblocks; import java.nio.file.Files; import java.nio.file.Paths; String apiKey = ""; String secretKey = Files.readString(Paths.get("fireblocks_secret.key")); ConfigurationOptions configurationOptions = new ConfigurationOptions() .basePath(BasePath.Sandbox) .apiKey(apiKey) .secretKey(secretKey); Fireblocks fireblocks = new Fireblocks(configurationOptions); ``` Replace `` with your API Key from the Fireblocks Console. In the TypeScript example, set `basePath` to the environment your workspace is in: `BasePath.Sandbox` for a Sandbox workspace, or `BasePath.US`, `BasePath.EU` or `BasePath.EU2` for a production workspace in that region. Alternatively, leave `basePath` out and set the `FIREBLOCKS_BASE_PATH` environment variable to one of those URLs. **CLI** Install the Fireblocks CLI (Node.js 18+ required), then connect it to your workspace: ```bash theme={"system"} npm install -g @fireblocks/fireblocks-cli fireblocks --version fireblocks configure # paste API key + path to fireblocks_secret.key fireblocks whoami # verify credentials resolve and authenticate ``` Other installers (Homebrew, standalone binaries, yarn/pnpm) are documented on the [Fireblocks CLI](/docs/fireblocks-cli) page. > [!TIP] > > For agent workflows, your agent should run `fireblocks help-index` once to discover commands, and use `--dry-run` on writes. For idempotency, set `externalTxId` in the body when creating transactions and pass `--idempotency-key` on other writes. ## Step 3: Create a Vault Account A Vault Account is where your assets are securely stored. **SDK** ```typescript TypeScript theme={"system"} const vaultResponse = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name: "My First Vault" }, }); const vault = vaultResponse.data; if (!vault.id) throw new Error("Vault account id missing from response"); console.log(vault); ``` ```python Python theme={"system"} vault = fireblocks.create_vault_account("My First Vault") print(vault) ``` ```java Java theme={"system"} CreateVaultAccountRequest request = new CreateVaultAccountRequest().name("My First Vault"); VaultAccount vault = fireblocks.vaults() .createVaultAccount(request, UUID.randomUUID().toString()) .get() .getData(); System.out.println(vault); ``` **CLI** ```bash theme={"system"} fireblocks vaults create-vault-account \ --data '{"name":"My First Vault"}' \ --no-confirm ``` You can view the new Vault account in the Console under **Accounts** > **Vault**. > [!TIP] > > If you’re using a Sandbox, your account should come pre-funded. If you need additional funds, see [Receiving funds into your Fireblocks account](https://support.fireblocks.io/hc/en-us/articles/4414266562450-Receiving-funds-into-your-Fireblocks-accounts) for more information. ## Step 4: Create a Transaction Now that your vault has funds, you can create your first transaction. **SDK** ```typescript TypeScript theme={"system"} const transactionResponse = await fireblocks.transactions.createTransaction({ transactionRequest: { assetId: "ETH_TEST", source: { type: TransferPeerPathType.VaultAccount, id: vault.id }, destination: { type: TransferPeerPathType.ExternalWallet, id: "" }, amount: "0.01", externalTxId: "", // Optional - Your unique ID; Fireblocks rejects later requests with the same value }, }); const transaction = transactionResponse.data; console.log(transaction); ``` ```python Python theme={"system"} transaction = fireblocks.create_transaction({ "assetId": "ETH_TEST", "source": { "type": "VAULT_ACCOUNT", "id": vault["id"] }, "destination": { "type": "EXTERNAL_WALLET", "id": "" }, "amount": "0.01", "externalTxId": "" # Optional - Your unique ID; Fireblocks rejects later requests with the same value }) print(transaction) ``` ```java Java theme={"system"} TransactionRequest transactionRequest = new TransactionRequest() .assetId("ETH_TEST") .source(new SourceTransferPeerPath() .type(TransferPeerPathType.VAULT_ACCOUNT) .id(vault.getId())) .destination(new DestinationTransferPeerPath() .type(TransferPeerPathType.EXTERNAL_WALLET) .id("")) .amount(new TransactionRequestAmount("0.01")) .externalTxId(""); // Optional - Your unique ID; Fireblocks rejects later requests with the same value CreateTransactionResponse transaction = fireblocks.transactions() .createTransaction(transactionRequest, null, null) // Use externalTxId, not an idempotency key, for transactions .get() .getData(); System.out.println(transaction); ``` **CLI** Preview the request first with `--dry-run`, then execute it. The unique `externalTxId` in the body makes retries safe, because Fireblocks rejects any later transaction with the same value: ```bash theme={"system"} # Preview fireblocks transactions create-transaction \ --data '{"assetId":"ETH_TEST","amount":"0.01","externalTxId":"","source":{"type":"VAULT_ACCOUNT","id":"0"},"destination":{"type":"EXTERNAL_WALLET","id":""}}' \ --dry-run # Execute fireblocks transactions create-transaction \ --data '{"assetId":"ETH_TEST","amount":"0.01","externalTxId":"","source":{"type":"VAULT_ACCOUNT","id":"0"},"destination":{"type":"EXTERNAL_WALLET","id":""}}' \ --no-confirm ``` ## Step 5: Verify Transactions To verify your transaction status, use the following. **SDK** For Fireblocks transactions: ```typescript TypeScript theme={"system"} const tx = (await fireblocks.transactions.getTransaction({ txId })).data; ``` ```python Python theme={"system"} tx = fireblocks.get_transaction_by_id(txId) ``` ```java Java theme={"system"} TransactionResponse tx = fireblocks.transactions().getTransaction(txId).get().getData(); ``` For external transactions: ```typescript TypeScript theme={"system"} const tx = (await fireblocks.transactions.getTransactionByExternalId({ externalTxId })).data; ``` ```python Python theme={"system"} tx = fireblocks.get_transaction_by_external_tx_id(externalTxId) ``` ```java Java theme={"system"} TransactionResponse tx = fireblocks.transactions().getTransactionByExternalId(externalTxId).get().getData(); ``` **CLI** ```bash theme={"system"} # By Fireblocks transaction ID fireblocks transactions get-transaction --tx-id --json # By external transaction ID fireblocks transactions get-transaction-by-external-id \ --external-tx-id --json ``` ## Next Steps **CLI** * [Fireblocks CLI](/docs/fireblocks-cli) — install, overview, and quick start * [CLI Authentication](/docs/cli-authentication) — credentials, profiles, and environment variables * [CLI Usage](/docs/cli-usage) — commands, flags, exit codes, and examples **SDKs** * [TypeScript SDK](/reference/typescript-sdk) * [Python SDK](/reference/new-python-sdk) * [Java SDK](/reference/java-sdk) **API and platform** * [API Reference](/reference/api-overview) — REST endpoints and request bodies * [Manage API keys](/docs/manage-api-keys) — CSR flow, roles, and rotation * Automate transaction signing with [API co-signers](/cosigners/overview/cosigner-architecture-overview) * Real-time updates with [webhooks](/reference/webhooks-gettingstarted-configuringwebhooks) # Overview Source: https://developers.fireblocks.com/docs/raw-signing > [!WARNING] > > Raw Signing is an insecure signing method and is not generally recommended. Bad actors can trick someone into signing a valid transaction message and use it to steal funds. > > For this reason, Raw Signing is a premium feature that requires an additional purchase and is not available in workspaces by default. > > If you're interested in this feature and want to see if your use case is eligible for it, please contact your Customer Success Manager. > > Fireblocks Sandbox workspaces have Raw Signing enabled by default to allow for testing purposes. Raw Signing is a powerful feature within Fireblocks. As the Web3 world continues to evolve rapidly, Fireblocks provides the option to support additional chains and actions by signing an arbitrary message using the Fireblocks infrastructure. With the Raw Signing API, users can generate ECDSA and EdDSA signatures to sign any transaction type or message, unlocking almost any possible use case, including staking, tokenization, and so on. Our WalletConnect integration and the Fireblocks Chrome Extension sometimes use Raw Signing to sign transactions initiated by dApps. You will need to add Policy rules for these dApp-initiated raw transactions to go through. For more details, see the [Raw Signing Developer Guide](/transactions/raw-signing). # What are the use cases for Raw Message Signing? Typically, Raw Message Signing is used in the following scenarios: * When you want to sign a transaction or manage tokens on a blockchain that Fireblocks doesn’t currently support * When you want to perform an operation that we don’t currently support on a blockchain that we do support (e.g., staking on a lesser-known blockchain) * When you want to use cryptography to prove and validate messages that are not supported by [Typed Message Signing](/docs/typed-message-signing-1) (e.g., Proof of Assets, Proof of Addresses) * When someone sends funds to your address over a blockchain that we don’t currently support, and you want to recover the funds # Enabling Raw Signing By default, Raw Signing is not available in production workspaces. Contact your Customer Success Manager to enable Raw Signing. If Raw Signing is disabled in your workspace and you attempt to create a raw transaction, it will fail and show the **BLOCKED\_BY\_POLICY** substatus. # Policy rules for Raw Signing Policies reject all raw transactions by default. After enabling the Raw Signing feature in your production workspace, you must add Policy rules that allow users to initiate, approve, and sign raw transactions from specific vault accounts. You can also use your Policy rules to limit the range of derivation paths, vault accounts, and assets available for raw transactions. Unless explicitly defined otherwise in the rule, the rule matches all derivation paths. When creating the rule, select Groups and accounts as your source and enter Any vault. Then you can enter a derivation path. The derivation path used in signing can be passed along with the signing request in one of two ways: * **Explicitly:** By passing the signing algorithm and the full derivation path * **Implicitly:** By passing the vault account ID, the asset ID, and (optionally) the change and the address index These properties together comprise a full BIP44-like derivation path. Typically, this approach is used to create custom transactions on supported protocols. # Read Canton Transactions Source: https://developers.fireblocks.com/docs/read-canton-transactions Canton items are standard Fireblocks transactions. Read them with the existing transactions endpoints and branch on the cantonDetails object. Canton items are Fireblocks transactions, not a separate resource tree. There are no Canton-specific read endpoints. You list and read Canton activity with the transactions endpoints you already use, and everything Canton-specific arrives in a `cantonDetails` object on the transaction. | What you want | Endpoint | | - | - | | List or filter Canton items | [`GET /v1/transactions`](/api-reference/transactions/get-transaction-history) | | Read one Canton item | [`GET /v1/transactions/{txId}`](/api-reference/transactions/get-a-specific-transaction-by-fireblocks-transaction-id) | The webhook carries the identical `cantonDetails` object, so a client that handles one needs no second parser for the other. See [Canton Transaction Webhooks](/docs/canton-transaction-webhooks). ## Identify a Canton transaction The presence of `cantonDetails` is the authoritative signal that a transaction is a Canton item. A transaction with no `cantonDetails` object is not one. ## The three shapes of cantonDetails At most one of these three keys is present on any transaction. | Key | Meaning | | - | - | | `cantonDetails.offerResponse` | This transaction belongs to an offer conversation: the offer, the answer to it, or a leg that cannot be answered. | | `cantonDetails.call` | This transaction is a call you made. | | `cantonDetails.settlement` | A venue settled an allocation. This is the credit side of a delivery-versus-payment. | The split is not inbound versus outgoing. Every response transaction you create is outgoing and carries `offerResponse`, and in the vault-to-vault transfer case a pre-existing outgoing transfer is stamped with a populated `availableResponses`. If `cantonDetails` is present but none of the three keys are, the transaction is Canton-related but outside these shapes. Skip it rather than treating it as an error. ## Decide whether an offer can be responded to This is the rule the rest of the page depends on. > [!WARNING] > > A transaction is answerable if and only if `cantonDetails.offerResponse.availableResponses` is non-empty. Never decide answerability from `subStatus == PENDING_RECEIVER_APPROVAL`. One offer can flag two transactions with `PENDING_RECEIVER_APPROVAL`. In the vault-to-one-time-address transfer case you see this: ```text theme={"system"} tx A PENDING_RECEIVER_APPROVAL availableResponses [TRANSFER_ACCEPT, TRANSFER_REJECT] tx B PENDING_RECEIVER_APPROVAL availableResponses [] originalTransactionId → tx A ``` A client that filters on the sub-status posts to both and gets a `409` on the second. A client that filters on `availableResponses.length > 0` posts once. Transaction B is neither broken nor a duplicate. It is a linked leg whose only job is to point at where the answer goes. ```javascript theme={"system"} const answerable = transactions.filter( (tx) => (tx.cantonDetails?.offerResponse?.availableResponses?.length ?? 0) > 0 ); ``` An empty `availableResponses` means nothing is answerable here. It does not always mean the offer is closed: on a linked leg it means answer elsewhere, and `originalTransactionId` says where. The array going empty is also the terminal signal, covering answered, expired, and a response in flight. There is no separate resolved flag. ## Fields on offerResponse | Field | Required | Description | | - | - | - | | `version` | Yes | Schema version of this object. Currently `1`. | | `domain` | Yes | `ONBOARDING`, `ALLOCATIONS`, or `TRANSFERS`. Copy this value straight into the `domain` field of your response body. | | `availableResponses` | Yes | Response identifiers you can exercise on this transaction right now. Identifiers only, never a schema. | | `vendor` | No | `DTCC` or `TRADEWEB`. Absent means vendor-neutral, not unknown. Allocations and transfers have no vendor. | | `expiresAt` | No | RFC 3339 date-time when the offer expires if unanswered. Absent on offer types that carry no deadline. | | `approvalTransactionId` | No | Set on the offer transaction once a response is dispatched. Names the outgoing response transaction. | | `originalTransactionId` | No | Set on the response transaction, pointing back at the offer it answered, and on a linked leg, pointing at where the answer belongs. | | `verdict` | No | `ACCEPTED` or `REJECTED`. Reflects the answer submitted, not whether the chain accepted it. The transaction status carries that. | > [!NOTE] > > Not every offer has a deadline, so treat `expiresAt` as optional. Where it is present and passes, the transaction moves to sub-status `RECEIVER_APPROVAL_TIMEOUT` and `availableResponses` empties. `availableResponses` carries identifiers, not argument shapes. What each value means, and which fields it requires, lives in the matching variant of the request schema on [Answer an offer](/api-reference/canton-beta/answer-an-offer). Treat the enum as open: a new vendor flow adds a value, so tolerate values you do not recognize rather than failing to deserialize. ## Answer an offer Take `id` from the answerable transaction. That is the `offerId` path parameter of [Answer an offer](/api-reference/canton-beta/answer-an-offer). ```http theme={"system"} POST /v1/operations/canton/offers/{offerId}/responses ``` Copy `cantonDetails.offerResponse.domain` into the body's `domain`, then pick a `responseType` from `availableResponses`. ```json theme={"system"} { "domain": "ONBOARDING", "response": { "responseType": "DTCC_END_INVESTOR_ONBOARDING_ACCEPT" } } ``` ## Fields on call Present on an outgoing transaction that carries a call you made with [Make a Canton call](/api-reference/canton-beta/make-a-canton-call). This makes `GET /v1/transactions` filterable into your Canton calls without a per-call-type collection endpoint. | Field | Required | Description | | - | - | - | | `version` | Yes | Schema version. Currently `1`. | | `domain` | Yes | The domain this call operates on. | | `type` | Yes | Which call this transaction carries, such as `DTCC_PARTICIPANT_ONBOARDING` or `ALLOCATION_WITHDRAW`. | | `vendor` | No | Absent on the vendor-neutral calls, `ALLOCATION_WITHDRAW` and `TRANSFER_WITHDRAW`. | | `originalTransactionId` | No | The transaction this call acts on. Present only on `ALLOCATION_WITHDRAW` and `TRANSFER_WITHDRAW`. | `domain` is derivable from `type`, but it is published so that filtering for all your onboarding activity is one filter across offers and calls rather than an eight-entry mapping each client reimplements. ## Fields on settlement Present on the settlement receipt, the transaction that appears when a venue executes an allocation you had locked. It is `COMPLETED` at creation and carries the cross leg: the asset and amount you receive, where the allocation transaction carried what you gave. | Field | Required | Description | | - | - | - | | `version` | Yes | Schema version. Currently `1`. | | `domain` | Yes | Always `ALLOCATIONS` today. | | `type` | Yes | `ALLOCATION_SETTLEMENT`. | | `originalTransactionId` | Yes | The allocation transaction this receipt settles. | Without `originalTransactionId` the credit side of a delivery-versus-payment is an unexplained incoming transfer, and the two halves of one trade cannot be reconciled through the API. ## The ALLOCATED lifecycle An accepted allocation is the one place a Canton transaction sits at `CONFIRMING` for an extended period rather than passing through it: ```text theme={"system"} status CONFIRMING · subStatus ALLOCATED locked on chain, withdrawable ↓ venue settles ↓ withdraw succeeds status COMPLETED status CANCELLED ``` `CONFIRMING` with `ALLOCATED` is the only state in which `ALLOCATION_WITHDRAW` accepts the transaction, and that transaction's `id` is what you pass as `allocationTransactionId` to [Make a Canton call](/api-reference/canton-beta/make-a-canton-call). ## Amounts `amount` is a decimal string, not a number. Token amounts exceed the precision of an IEEE 754 double, so a JSON number would round silently. Parse with a decimal library rather than `parseFloat`. Operations that move nothing, including onboarding, invites, and allow-list changes, carry `"0"`. # Segregate Duties Source: https://developers.fireblocks.com/docs/segregate-duties To ensure the security of your workspace operations, it is crucial to segregate duties and delegate responsibilities appropriately. The first and most important step is to distinguish between users handling critical managerial tasks and those managing regular operations. For the former, it is important to understand the permission level of an administrator and the influence they will have with any changes made to the workspace. Please review this [article](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles) for more information. For day-to-day operations, consider three key roles in the transaction process when assigning responsibilities: * Who will initiate transactions? * Who will approve transactions? * Who will sign off on transactions? Once you have defined these responsibilities, review the following use cases to help you segregate duties properly: **Manual Process:** Typically used for operations involving a large amount of assets. This involves 100% human intervention. A transaction will be initiated via the Fireblocks console UI, approved manually by certain people (user or group) on their mobile devices, and signed by either the same people or someone else. Note that approvers are not required to have an MPC key to approve transactions. **Semi-Automated Process:** Used for operations that are not large in value but still significant enough to require human verification. Transactions can be initiated by a person via the UI or an API user and signed by either one of them. **Fully Automated Process:** Commonly used for internal transactions (between Fireblocks VA) or withdrawals of small amounts. API users initiating transactions must have the relevant permissions. If the same user is linked with the API-cosigner machine, ensure they are assigned a signer permission role. ## Which API roles can initiate vs. sign transactions Signing is a distinct step from initiating a transaction. A Non-Signing Admin or Editor API user can still call `POST /transactions` to create a transaction, provided the [Transaction Authorization Policy (TAP)](/docs/set-transaction-authorization-policy) permits it. An Approver cannot initiate transactions, only approve them. A separate Signing API user (typically paired with an API Co-signer) then signs the initiated request based on the TAP rule. | API Key Type | Can initiate transactions | Can approve transactions | Can sign transactions | | - | - | - | - | | **Admin** | Yes | Yes | Yes | | **Signer** | Yes | Yes | Yes | | **Non-Signing Admin** | Yes (needs a designated signer) | Yes | No | | **Approver** | No | Yes | No | | **Editor** | Yes (needs a designated signer) | No | No | | **Viewer** | No | No | No | | **Security Auditor** | No | No | No | This lets you split responsibilities across API users. For example, an application server can use an Editor API key to initiate a transaction, while an API Co-signer paired with a Signer API user handles the actual signing. The TAP rule that matches the transaction ultimately controls whether a specific initiator, source, destination, and signer combination is allowed. Confirm your TAP rules include the initiator you intend to use. # Self-Custody Infrastructure Source: https://developers.fireblocks.com/docs/self-custody-infrastructure Be the owner of your funds without compromising speed and security using Fireblocks' self-custody infrastructure # What is Self-Custody Infrastructure? Fireblocks Self-Custody Infrastructure is a secure and reliable way to store your digital assets. With our robust APIs and multi-layer security, MPC, and Intel SGX, you can be sure that your funds are safe and accessible instead of handling the security work yourself. [Start developing on Fireblocks today](https://www.fireblocks.com/developer-sandbox-sign-up/?gl=1*19mlzew*_ga*MTY4ODc5Mjc3Ni\[%E2%80%A6]_ga_D39L1D2ZX2*MTY4Nzg2MTQwOS4xNi4xLjE2ODc4Njc3OTMuMjcuMC4w). # Set up and store your funds through Fireblocks Self-Custody Infrastructure Using our self-custody technology, you can harness your funds to hold them securely and safely: * **API Co-Signer:** Automate the approval and signing of transactions and maintain custody with our API Co-Signer. * **MPC technology:** With MPC, the corresponding private key shares are created and encrypted in isolated environments among multiple parties. To sign transactions, the key shares are used to perform multiple rounds of computation without ever being brought into the same environment. Because of this, MPC eliminates the single point of compromise of private key creation and signing. * **Secure Hardware Enclaves:** Self-custody your private keys in secure hardware enclaves to keep your keys safe from malicious software or unauthorized users. * **Key backups:** Get a backup copy of all your signing keys and have full control over your funds. Learn more about [our Custody Infrastructure](https://www.fireblocks.com/digital-asset-custody/). # Guides ## Raw Message Signing Support more chains and actions. Generate ECDSA and EdDSA signatures to [sign any transaction type or message](/docs/raw-signing). ## Typed Message Signing Allows you to [sign messages using specific standard formats](/docs/typed-message-signing-1) that prefix the message with a magic string. For ETH\_MESSAGE and EIP712 messages. ## Custodial Services Flexible MPC-based wallet infrastructure enables both [segregated vault structures and omnibus vault structures](/docs/create-direct-custody-wallets#types-of-vault-structures) based on your business needs. ## UTXO Consolidation Leverage automation to [consolidate your unspent UTXO balances across UTXO wallets](/reference/consolidate-utxos) through special transactions that merge them into a single output. ## UTXO Manual Selection Use dedicated API calls to [manually select specific UTXO asset wallet balances](/reference/select-utxos-for-a-transaction) to consolidate into a single transaction to send to your omnibus account. ## Creating an Omnibus Vault Structure Use API keys to [generate intermediate vault accounts, identify incoming transactions, and sweep funds](/reference/create-omnibus-structure) to the Omnibus Deposits vault accounts. ## Sweep to Omnibus How to [automate moving funds from intermediate vault accounts, assigned to your end-users](/reference/sweep-to-omnibus-1) for their deposits to your omnibus account. ## Validating Travel Rule transactions with Fireblocks and Notabene Using the Fireblocks SDK to [submit transactions to Notabene for validation that they comply with the Travel Rule](/docs/notabene) while keeping PII data encrypted. # Developer Community Want to learn more from Fireblocks knowledge experts and other developers? [Join our developer community today](https://community.fireblocks.com/)! # Set Auto Fueling Property Source: https://developers.fireblocks.com/docs/set-auto-fueling-property Vault account auto-fueling is available once the Gas Station is enabled and you have designated a [Gas Station tank](/docs/enabling-the-gas-station-1#designate-the-gas-station-tank) in **Settings** > **Gas Station tank**. Any vault account with the Gas Station badge in the Console participates in the Gas Station service. * When an incoming token transaction to an auto-fueling vault account completes, Fireblocks automatically transfers the appropriate base asset from the Gas Station tank to the vault account. This is because the auto-fueled vault account must have a token with a non-zero balance on the relevant network where you are expecting an auto-fueling transaction. * When an outgoing transaction from an auto-fueling vault account completes, Fireblocks checks the token and gas balance. The vault account is refueled if the token balance is above 0.00001 and the base asset is below the set threshold. Token transfers from this vault account to any destination will then have sufficient gas to cover transaction fees. ## Enabling auto-fueling in the Console For new vault accounts, select **Auto-fuel with gas after an incoming transaction completes** when you create the account. For existing vault accounts, go to **My Funds** > **Accounts**, find the appropriate vault account, and then select **More Actions** (**...**) > **Enable Auto-Fueling** > **Enable**. The Gas Station badge appears next to the name of the vault account when you're done. ## Disabling auto-fueling in the Console In the Fireblocks Console, go to **My Funds** > **Accounts**, find the appropriate vault account, and then select **More Actions** (**...**) > **Disable Auto-Fueling** > **Disable**. The Gas Station badge is removed when you're done. ## Enabling auto-fueling via the API For new vault accounts, call the [Create a new vault account endpoint](/reference/createvaultaccount) and set the **autoFuel** field to **true**. For existing vault accounts: 1. Call the [List vault accounts (paginated) endpoint](/reference/getpagedvaultaccounts) to find the vault account's ID. 2. Call the [Turn auto-fueling on or off endpoint](/reference/setvaultaccountautofuel), enter the vault account's ID in the **vaultAccountId** field, and then set the **autoFuel** field to **true**. ## Disabling auto-fueling via the API 1. Call the [List vault accounts (paginated) endpoint](/reference/getpagedvaultaccounts) to find the ID of the vault account. 2. Call the [Turn auto-fueling on or off endpoint](/reference/setvaultaccountautofuel), enter the vault account's ID in the **vaultAccountId** field, and then set the **autoFuel** field to **false**. > [!NOTE] > > Check the Gas Station [developer guide](/reference/enable-auto-fueling) for API code examples. # Set Policies Source: https://developers.fireblocks.com/docs/set-transaction-authorization-policy Policies define which actions are allowed or blocked in your Fireblocks workspace: controlling who can initiate transactions, from which sources, to which destinations, and under what conditions. You can manage Policies directly in the Fireblocks Console using the Policy Editor, or programmatically via the API. ## Managing policies via the API The Fireblocks API gives you two ways to manage your Policy: publish changes directly, or work with a draft before publishing. ### Publish directly To retrieve your active Policy or publish a new one without a review step, use these endpoints: * [Get the active policy](/api-reference/policy-editor-v2/get-the-active-policy-and-its-validation-by-policy-type) * [Publish a new set of policy rules](/api-reference/policy-editor-v2/send-publish-request-for-a-certain-draft-id) ### Work with drafts If your workflow requires reviewing or validating Policy changes before they go live, use Policy Drafts: * [Get the active draft](/api-reference/policy-editor-v2/get-the-active-draft-by-policy-type) * [Update the draft with a new set of rules](/api-reference/policy-editor-v2/update-the-draft-with-a-new-set-of-rules-by-policy-types) * [Publish a draft by ID](/reference/publishpolicyrules) ## Best practices Policy rules are evaluated in the order they are defined, so rule ordering matters. Place the most restrictive rules first, and order rules by likelihood of a match. This ensures high-risk or tightly scoped rules are evaluated before broader, more permissive ones, reducing the chance of a transaction being incorrectly approved. ## Learn more * [About Policies](https://support.fireblocks.io/hc/en-us/articles/29184395887772-About-Policies): Help Center overview of how Policies work * [Policies developer guide](/reference/configure-transaction-authorization-policy): How to work with Policies programmatically * [Tags](/docs/tags): Create protected tags you can reference in Policy rules # Solana Gasless Source: https://developers.fireblocks.com/docs/solana-gasless Send SPL tokens from vault accounts with no SOL by setting a fee payer vault account. Covers setup, the key set requirement, useGasless, and feePayerInfo. ## Overview New to Solana Gasless? See [Configuring Solana Gasless transactions](https://support.fireblocks.io/hc/en-us/articles/20170988182300) in the Help Center for the Console walkthrough, then use this guide for the API behavior. Solana Gasless lets a vault account send SPL tokens without holding SOL. It uses Solana's native fee payer: one vault account in your workspace is set as the *fee payer* and pays the SOL fee for every gasless transaction. Each transaction is signed by two vault accounts, the source (token owner) and the fee payer. It does not cover SOL transfers. For how gasless compares across chains, see the [Gasless Transactions Overview](/docs/gasless-transactions). ## Requirements * **A fee payer vault account** in your workspace, holding enough SOL to pay fees. The fee payer must be in the same workspace. External workspaces, the Fireblocks relay, and Wallet Pools are not available as the fee payer on Solana. * **Same key set.** The source vault account must share a key set with the fee payer vault account. ## Step 1. Set the fee payer in the Console Go to **Settings** > **Initiate gasless transactions**, turn on **Solana**, select the fee payer vault account, then select **Save**. Solana has no default-mode setting. Once a fee payer is set, eligible SPL transfers from other vault accounts use it automatically. ## Step 2. Send a transaction Submit a standard SPL token transfer with [Create a new transaction](/reference/createtransaction). You need no extra fields. `POST /v1/transactions` ```jsonc theme={"system"} { "assetId": "SOL_USDC_PTHX", "amount": "250", "source": { "type": "VAULT_ACCOUNT", "id": "42" }, "destination": { "type": "ONE_TIME_ADDRESS", "oneTimeAddress": { "address": "7xKX..." } }, "useGasless": false // Optional - Set false to pay the fee from the source vault account instead } ``` **Field notes:** * `useGasless`: omit it to use the fee payer. Set `false` to have the source vault account pay its own fee. * `source`: the vault account needs the token balance only. When the source is the fee payer itself, the transaction is sent as a standard transaction. * `assetId`: an SPL token. SOL transfers are not covered. ## Step 3. Confirm the fee payer paid [Get a transaction](/api-reference/transactions/get-a-specific-transaction-by-fireblocks-transaction-id) or read the [transaction webhook](/reference/monitoring-transaction-status). The fee payer is reported in `feePayerInfo`. ```json theme={"system"} { "id": "9d3a...", "status": "COMPLETED", "assetId": "SOL_USDC_PTHX", "feePayerInfo": { "feePayerAccountId": "3" } } ``` | Field | Description | | - | - | | `feePayerInfo.feePayerAccountId` | The vault account that paid the fee. | ## Troubleshooting | Symptom | Cause | Fix | | - | - | - | | Error `1448` "Fee payer is not allowed for asset" | The asset does not support a fee payer | Check that the asset is an SPL token. | | Error `1449` "Fee payer can't pay" | The fee payer has no SOL wallet or not enough SOL | Fund the fee payer vault account with SOL. | See [API error codes](/reference/api-error-codes) for the full list. ## Related * [Gasless Transactions Overview](/docs/gasless-transactions): compare gasless mechanisms across chains. * [EVM Gasless (Universal Gasless)](/docs/evm-gasless): gasless transactions on EVM chains. * [Monitoring transaction statuses](/reference/monitoring-transaction-status): track transactions through webhooks. # Stake Assets Source: https://developers.fireblocks.com/docs/stake-assets You can stake to various networks from your Vault using the Fireblocks API. When staking, you select a third-party staking provider to operate the validators associated with your staked funds. Typically, the staked amount is locked for a certain period of time. Staked assets become eligible to receive rewards after the network's activation period. Staking rewards are calculated over a period of time usually known as an epoch. This is the period of time in which validators and their staked amount are accounted for. Depending on the network, an epoch may be anywhere from a few minutes to several days. Typically, staking rewards are calculated and distributed after each epoch ends. When you unstake your assets, you no longer generate staking rewards, but you're able to use those assets for other transactions. # Staking via Fireblocks API To stake, you must fund a vault account with the asset you want to stake and create Policy rules for staking. Then create staking transactions using the dedicated [staking API endpoints](/reference/getchains). Staking with the Fireblocks API allows you to: * Easily and securely stake assets through native API calls. * Create dedicated Policy rules for staking using the **Stake** operation type. * Track the status and rewards for your staked funds. * Initiate staking transactions without the need for SDKs, scripts, or Raw Signing. * Select validators to oversee your staked funds. ## Supported assets * Ethereum * Solana * MATIC For more details, see the [Staking developer guide](/reference/stake-assets-1). # Sumsub Source: https://developers.fireblocks.com/docs/sumsub Connect Sumsub through the Fireblocks API, then create, encrypt, and link Travel Rule messages and resolve missing or incomplete ones. Sumsub is a Travel Rule provider. It is a premium add-on that Fireblocks Support enables on Developer Sandbox, Testnet, and Mainnet workspaces. Everything, including connecting and creating Travel Rule messages (TRMs), happens through the API: you encrypt the PII on your side, and because Fireblocks does not hold the key that decrypts it, there is no Console flow. Rules are set by Fireblocks Support. ## What you can do through the API Paths marked `…` start with `/screening/trlink/customers/integration/{customerIntegrationId}`. | Step | Task | Console | API | | - | - | - | - | | 1 | Set up the Fireblocks SDK in your system | No | No API call; install the TypeScript SDK | | 2 | Create a Travel Rule legal entity | No | [`POST /screening/trlink/customers`](/api-reference/trlink/create-customer) | | 2 | List Travel Rule partners and their identifiers | No | [`GET /screening/trlink/partners`](/api-reference/trlink/list-available-trsupport-partners) | | 2 | Create the integration | No | [`POST /screening/trlink/customers/integration`](/api-reference/trlink/create-customer-integration) | | 2 | Test the connection | No | [`POST …/test_connection`](/api-reference/trlink/test-connection) | | 3 | Read the policy | No | [Get TRLink policy](/api-reference/trlink/get-trlink-policy) | | 3 | Change the policy | Through Fireblocks Support | No | | 4 | Get Sumsub's public key | No | [`GET …/public_key`](/api-reference/trlink/get-public-key-for-pii-encryption) | | 5 and 6 | Assess whether a transaction needs a TRM | No | [`POST …/trm/assess`](/api-reference/trlink/assess-travel-rule-requirement) | | 5 and 6 | Create a TRM | No | [`POST …/trm`](/api-reference/trlink/create-travel-rule-message) | | 5 | Create the transaction with the TRM attached | No | `travelRuleMessageId` in [`POST /transactions`](/api-reference/transactions/create-a-new-transaction) | | 5 and 6 | Link a TRM to a transaction | No | [`POST /screening/trlink/transaction/{txId}/travel_rule_message_id`](/api-reference/trlink/set-transaction-travel-rule-message-id) | | 5 | Link a TRM to one destination | No | [`POST /screening/trlink/transaction/{txId}/destination/travel_rule_message_id`](/api-reference/trlink/set-destination-travel-rule-message-id) | | Resolve messages | Get a TRM's required actions | No | [Get required actions for a TRM](/api-reference/trlink/get-required-actions-for-a-trm) | | Resolve messages | Resolve a required action | No | [Resolve action for a TRM](/api-reference/trlink/resolve-action-for-a-trm) | | Resolve messages | Decide on a transaction that has no TRM | No | [`POST …/transactions/{txId}/manual_decision`](/api-reference/trlink/manual-decision-for-missing-trm) | | Resolve messages | Get a TRM's status and details | No | [`GET …/trm/{trmId}`](/api-reference/trlink/get-trm-by-id) | | Resolve messages | Cancel a TRM and notify the counterparty | No | [`POST …/trm/{trmId}/cancel`](/api-reference/trlink/cancel-travel-rule-message) | | Any time | Redirect a TRM to a subsidiary VASP | No | [`POST …/trm/{trmId}/redirect`](/api-reference/trlink/redirect-travel-rule-message) | | Any time | List or get VASPs in Sumsub's directory | No | [`GET …/vasps`](/api-reference/trlink/list-vasps), [`GET …/vasps/{vaspId}`](/api-reference/trlink/get-vasp-by-id) | | Any time | Check whether Sumsub supports an asset | No | [`GET …/assets`](/api-reference/trlink/list-supported-assets), [`GET …/assets/{assetId}`](/api-reference/trlink/get-supported-asset-by-id) | | Any time | List your Travel Rule legal entities | No | [`GET /screening/trlink/customers`](/api-reference/trlink/get-all-customers) | | Any time | Get a Travel Rule legal entity | No | [`GET /screening/trlink/customers/{customerId}`](/api-reference/trlink/get-customer-by-id) | | Any time | Update a Travel Rule legal entity | No | [`PUT /screening/trlink/customers/{customerId}`](/api-reference/trlink/update-customer) | | Any time | Delete a Travel Rule legal entity | No | [`DELETE /screening/trlink/customers/{customerId}`](/api-reference/trlink/delete-customer) | | Any time | List a legal entity's integrations | No | [`GET /screening/trlink/customers/{customerId}/integrations`](/api-reference/trlink/get-customer-integrations) | | Any time | Get an integration | No | [`GET /screening/trlink/customers/{customerId}/integrations/{customerIntegrationId}`](/api-reference/trlink/get-customer-integration-by-id) | | Any time | Disconnect | Through Fireblocks Support | [`DELETE /screening/trlink/customers/integration/{customerIntegrationId}`](/api-reference/trlink/disconnect-customer-integration) | ## Step 1: Set up the Fireblocks SDK Install the Fireblocks SDK in your system; TRMs are created and linked from your code. See [TypeScript SDK](/reference/typescript-sdk). Before you continue: * Sumsub Travel Rule is enabled on your Sumsub account. See [Sumsub's Fireblocks integration guide](https://docs.sumsub.com/docs/fireblocks-integration). * Fireblocks Support has enabled Sumsub on your workspace. * You have downloaded your key pair from Sumsub. You sign the data you send with your private key, and decrypt the data you receive with it. ## Step 2: Connect Sumsub You do this once, during onboarding. > [!NOTE] > > The Travel Rule legal entity you create here is separate from the legal entity you register in [Address Registry](/reference/legal-entity-setup). Creating one does not create or change the other. 1. **Create a Travel Rule legal entity**, the business subject to the Travel Rule. Most customers need one; if you operate across jurisdictions with different requirements, create one per jurisdiction and assign vault accounts to each. Leave exactly one entity without a vault restriction: it covers every vault account not assigned elsewhere. | Field | Description | | - | - | | `shortName` | Required. | | `fullLegalName` | Full registered name. | | `countryOfRegistration` | ISO 3166-1 alpha-2 code, for example `US`. | | `nationalIdentification` | National identifier, for example an EIN. | | `dateOfIncorporation` | ISO date, for example `2020-01-15`. | | `geographicAddress` | Registered address. | | `vaults` | Vault account IDs this entity covers. | | `discoverable` | `anonymous` (default): counterparties cannot see your details, but Fireblocks confirms the address exists. `hidden`: nothing is disclosed. `discoverable`: full details are shared. | 2. **Create the integration** with the legal entity's `customerId` and Sumsub's `partnerIdent` from the partner list. The response returns a `customerIntegrationId`, used in every call after this. 3. **Share the `customerIntegrationId` with Sumsub.** Sumsub supports auto-connect only: it sends your API credentials to Fireblocks directly. 4. **Test the connection.** `success: true` confirms the integration can reach Sumsub. To update your Sumsub credentials later, submit a ticket to Fireblocks Support. Several Fireblocks workspaces can use one Sumsub account. ## Step 3: Set up the policy Sumsub uses three policy stages: Trigger (Screening Policy), Missing Travel Rule Message, and Outcome (Post-Screening Policy); see [Compliance Policies](/docs/compliance-policies). Missing Travel Rule Message rules can depend on how long a transaction has waited, for example accepting small transactions after 30 minutes but rejecting large ones. Rules are not self-service: fill out a policy template, run it through your compliance approval process, and submit it to Fireblocks Support, who apply it. Read the current policy through the API. Download each policy template and its instructions as CSV files: * Screening Policy: [template (CSV)](/files/policy-templates/screening-policy-template.csv) and [instructions (CSV)](/files/policy-templates/screening-policy-instructions.csv) * Missing Travel Rule Message Policy: [template (CSV)](/files/policy-templates/missing-travel-rule-message-policy-template.csv) and [instructions (CSV)](/files/policy-templates/missing-travel-rule-message-policy-instructions.csv) * Post-Screening Policy: [template (CSV)](/files/policy-templates/post-screening-policy-template.csv) and [instructions (CSV)](/files/policy-templates/post-screening-policy-instructions.csv) Fireblocks enforces limits you cannot configure, so a transaction never waits indefinitely: 1.5 hours for outgoing transactions, 7 days for incoming. Near the limit, a Wait rule is skipped instead of holding the transaction. A transaction's screening status is Completed, Pending, Bypassed (approved without review by your Trigger), or Failed. In a multi-destination transaction, each destination is evaluated separately and the most restrictive result applies to the whole transaction. ### Policy examples Each stage is a list of rules. Fireblocks evaluates the rules from top to bottom, and the first match wins. A rule can be scoped by direction, asset, amount, customer, provider, and source or destination type. Rule shape for each stage: ```ts theme={"system"} // Trigger (Screening Policy): which transactions require screening interface PreScreeningRule { // Matching criteria customerId?: string; direction?: "inbound" | "outbound"; asset?: string; amount?: { range?: { min?: number; max?: number; }; currency: "USD" | "NATIVE"; }; sourceType?: string; destType?: string; // Required action action: "SCREEN" | "PASS"; } // Missing Travel Rule Message: what to do while no message exists interface MissingTRMRule { // Matching criteria: same as the Trigger, plus timing constraints validBefore?: number; // seconds validAfter?: number; // seconds // Required action action: "WAIT" | "ACCEPT" | "REJECT"; } // Outcome (Post-Screening Policy): how to respond to the screening result interface PostScreeningRule { // Matching criteria: same as the Trigger, plus the message status trmStatus?: "ACCEPTED" | "REJECTED" | "PENDING" | "FAILED"; // Timing constraints validBefore?: number; validAfter?: number; // Required action action: "ACCEPT" | "REJECT" | "ALERT" | "WAIT"; } ``` While a destination waits, two separate policies decide what happens: | Situation | Policy | Allowed actions | | - | - | - | | The message was screened and the result is still `PENDING` | Outcome (Post-Screening Policy) | `ACCEPT`, `REJECT`, `WAIT`, `ALERT` | | No Travel Rule Message exists yet | Missing Travel Rule Message | `WAIT`, `ACCEPT`, `REJECT` | The default is `WAIT`, then `ACCEPT`: hold for a window, then accept if nothing changes. Set the hold window with `validAfter` and `validBefore`, in seconds from the start of the screening step. ```ts theme={"system"} // Wait, then accept (default behavior) const waitThenAccept = [ { action: "WAIT", validBefore: 3600 }, // hold up to 1 hour { action: "ACCEPT" }, // then accept ]; // Accept immediately (no hold) const acceptImmediately = [{ action: "ACCEPT" }]; // Block on fallthrough instead of accepting const waitThenReject = [ { action: "WAIT", validBefore: 3600 }, { action: "REJECT" }, ]; ``` To record Travel Rule data without ever holding or blocking a transaction, set both policies to a single `ACCEPT` rule, with no `WAIT`: ```ts theme={"system"} const postScreening = [{ action: "ACCEPT" }]; // PENDING: accept const missingTrm = [{ action: "ACCEPT" }]; // no message: accept ``` ## Step 4: Encrypt the PII Sumsub uses a nested JWT: a JWS signed with your private key, inside a JWE encrypted with Sumsub's public key. | | Value | | - | - | | Signature | `RS256`, with your private key | | Key encryption | `RSA-OAEP-256`, with Sumsub's public key | | Content encryption | `A256GCM` | | Sumsub's public key | Returned as a JWK with `use: "enc"`. Fetch it before each encryption, since keys can rotate. | The encrypted result goes in the TRM's `ivms101` object. `filledFields` lists, in dot notation, every IVMS101 field you populated: ```json theme={"system"} { "version": "IVMS101.2023", "data": "", "filledFields": [ "Originator.originatorPerson[].naturalPerson.name.nameIdentifier[].primaryIdentifier", "Beneficiary.beneficiaryPerson[].naturalPerson.name.nameIdentifier[].primaryIdentifier" ] } ``` To read data you receive, decrypt the JWE with your private key and verify the inner JWS with Sumsub's public key. > **Reference implementation: JWT encryption and decryption** > > Below is an example of how to encrypt and decrypt IVMS data exchanged with Sumsub. To encrypt, sign the IVMS data with your private key, then encrypt it with Sumsub's public key. To decrypt, use your own private key to decrypt the data, then verify the signature with Sumsub's public key. Your key pair is generated by Sumsub on their marketplace and available to download there. > > > [!NOTE] > > > > Custom key pair upload is under consideration for future releases. > > #### Encryption example > > Sumsub's public key response looks like this: > > ```json theme={"system"} > { > "issuer": "sumsub.com", > "publicKey": { > "kty": "RSA", > "kid": "test-sender-2025", > "e": "AQAB", > "n": "q6fmy_rjX2xizMiXoWKRVZSrDXXYLnMk6J_q-5FAD5q7CdqK10XmQZrlzItLpj9qxl0LfcZhZ93PL55abei1F4u6Jdzh9LrfUF5WZ7Cvi25n6aeJoOd7JIERY0hP8s-xdiDrr5fRqdseHymENhvwK5J8_adEOcWHR_7ymb17c3ivFQmprytkJD6BfXaF7qGeE6cx0UiDlD1sHCGcd26I1JNjYQZJie0qlPT8x7jILNA7sW1_Nk8wx9jRDtUv6-K2qnz4MvFe1D6rddcnCsiX7MRPWKm10hy41MTJkQcWUYgtDqfdOXNo8D8BnaVuaLEsByNxMQiL8ePXZpBro0nrnw" > } > } > ``` > > Here is an example of a private key: > > ```json theme={"system"} > { > "kty": "RSA", > "kid": "test-recipient-2025", > "e": "AQAB", > "n": "m58UpcvahBK3-Uma_S4HTGwijmASh_3ATDwkjIb1svp2jnrv6lhjE2Yz28b0oUsEa7Wau6x0NPbaUCrQjjN5iUoumn4Jwq6jKttXFtd4v3583co4sBjGoPqk7GbkeYKzEOs2wmKgKbQjjk1BaHFpTPlAmS2JbNy6VmHNj-62BK1vIeegXheqxkr-b-MvESmL746juz_LlTMPmJqbaNuMeQMktT9lBLPfZermLfUuC7myuQhrsD8zJaNiNDWJQ6dzNi-Cy0uGlDcy9xtSQMW1Tv3Fc7hO_bRD1NI-sJp7nVi6hdc-QMOyrv8tzlhTV7gh3Wnn2kIFFkWy86gYYHw79Q", > "d": "", > "p": "", > "q": "", > "dp": "", > "dq": "", > "qi": "" > } > ``` > > Here is an example of source IVMS data: > > ```json theme={"system"} > { > "Originator": { > "originatorPerson": [ > { > "naturalPerson": { > "name": { > "nameIdentifier": [ > { > "primaryIdentifier": "Doe", > "secondaryIdentifier": "John", > "nameIdentifierType": "BIRT" > } > ], > "localNameIdentifier": [], > "phoneticNameIdentifier": [] > }, > "customerIdentification": "e8nvdf2vwa5036ix", > "dateAndPlaceOfBirth": { > "dateOfBirth": "1992-05-08" > } > } > } > ], > "accountNumber": [] > }, > "Beneficiary": { > "beneficiaryPerson": [ > { > "naturalPerson": { > "name": { > "nameIdentifier": [ > { > "primaryIdentifier": "Doe", > "secondaryIdentifier": "Jack", > "nameIdentifierType": "BIRT" > } > ], > "localNameIdentifier": [], > "phoneticNameIdentifier": [] > }, > "customerIdentification": "67b4defbfa5d2e41d8177ab3", > "dateAndPlaceOfBirth": { > "dateOfBirth": "1991-04-07" > } > } > } > ], > "accountNumber": [] > } > } > ``` > > This utility takes the paths to Sumsub's public key, your private key, and the IVMS data, and produces an encrypted IVMS block with its filled fields: > > ```javascript theme={"system"} > const jose = require('node-jose'); > const crypto = require('crypto'); > const path = require('path'); > const fs = require('fs/promises'); > > const SIGNATURE_ALGORITHM = 'RS256'; > const ENCRYPTION_ALGORITHM = 'RSA-OAEP-256'; > const ENCRYPTION_METHOD = 'A256GCM'; > const DEFAULT_EXPIRY_MS = 300000; // 5 minutes > const CLOCK_SKEW_SEC = 60; // ±60s skew > const CLAIM_DATA = 'data'; // The JWT claim holding the payload > > /** > * Travel Rule JWT Codec for PII Data Encryption > * > * Implements a nested JWT (JWS signed token encrypted inside a JWE) > * for secure PII exchange, compatible with the Java TRJWTCodec implementation. > * > * Features: > * - Object signing and encryption: `prepareIvmsData` / `decryptAndDecode` > * - JTI (JWT ID) claim for replay attack protection > * - Standard claims validation (iss, iat, exp) > */ > class TRJWTCodec { > constructor() { > this.keyStore = jose.JWK.createKeyStore(); > this.publicKey = null; // Partner's public key (for encryption) > this.privateKey = null; // Our private key (for signing) > this.initialized = false; > } > > /** > * Initialize the codec with RSA key pairs. > * > * @param {Object} config - Configuration object > * @param {string} config.publicKey - PEM-encoded RSA public key (partner's key for encryption) > * @param {string} config.privateKey - PEM-encoded RSA private key (our key for signing) > */ > async initialize(config) { > this._assert(config.publicKey, 'Public key is required'); > this._assert(config.privateKey, 'Private key is required'); > > try { > this.publicKey = await this.keyStore.add(config.publicKey); > this.privateKey = await this.keyStore.add(config.privateKey); > this.initialized = true; > } catch (error) { > throw new Error(`Failed to initialize TRJWTCodec with provided keys: ${error.message}`); > } > } > > /** > * Encrypt and sign an object as a nested JWT (JWS inside JWE). > * This is the lower-level function that returns only the JWE string. > * > * @param {Object} data - The JSON-serializable object to encrypt. > * @param {string} issuer - The 'iss' claim (our identifier). > * @param {number} [expiryMs=DEFAULT_EXPIRY_MS] - Expiry time in milliseconds. > * @returns {Promise} - The compact-serialized JWE token string. > */ > async encodeAndEncrypt(data, issuer, expiryMs = DEFAULT_EXPIRY_MS) { > this._assertInitialized(); > this._assert(data, 'Data cannot be null or undefined'); > this._assert(issuer && typeof issuer === 'string' && issuer.trim(), 'Issuer must be a non-empty string'); > > // 1. Serialize the object to a JSON string > const dataJson = JSON.stringify(data); > // 2. Encode the JSON string as base64url > const dataB64Url = Buffer.from(dataJson, 'utf8').toString('base64url'); > // 3. Pass the base64url string to the byte-level encryption method > return this.encryptBytes(dataB64Url, issuer, expiryMs); > } > > /** > * Prepares the full IVMS data object, including the encrypted JWE > * and the list of filled fields. > * > * @param {Object} data - The IVMS data object to encrypt. > * @param {string} issuer - The 'iss' claim (our identifier). > * @param {number} [expiryMs=DEFAULT_EXPIRY_MS] - Expiry time in milliseconds. > * @returns {Promise<{version: string, data: string, filledFields: string[]}>} > * An object containing the API version, the encrypted JWE string, > * and the dot-notation paths of all non-null fields. > */ > async prepareIvmsData(data, issuer, expiryMs = DEFAULT_EXPIRY_MS) { > // 1. Create the encrypted JWE string from the data object. > // This handles signing with our private key and encrypting with the partner's public key. > const encryptedData = await this.encodeAndEncrypt(data, issuer, expiryMs); > // 2. Extract all filled field paths from the original data object. > const filledFields = this._extractFilledFields(data); > > return { > version: 'IVMS101.2023', // API version > data: encryptedData, > filledFields: filledFields > }; > } > > /** > * Decrypt and decode data from an encrypted JWE token. > * > * @param {string} encryptedJWT - The compact-serialized JWE token. > * @param {string} [expectedIssuer] - (Optional) The expected 'iss' claim for validation. > * @returns {Promise} - The original decrypted and parsed JSON object. > */ > async decryptAndDecode(encryptedJWT, expectedIssuer = null) { > this._assertInitialized(); > this._assert(encryptedJWT, 'Encrypted JWT cannot be null or empty'); > > // --- JWE Decryption --- > // 1. Decrypt the outer JWE layer using our private key. > const jweResult = await jose.JWE.createDecrypt(this.privateKey) > .decrypt(encryptedJWT); > > // The payload of the JWE is the inner JWS. > const signedJWT = jweResult.payload.toString('utf8'); > > // --- JWS Verification --- > // 2. Verify the signature of the inner JWS using the partner's public key. > const jwsResult = await jose.JWS.createVerify(this.publicKey) > .verify(signedJWT); > > // The payload of the JWS is the claims object. > const claimsJson = jwsResult.payload.toString('utf8'); > const claims = JSON.parse(claimsJson); > > // --- Claims Validation --- > // 3. Validate standard JWT claims (iat, exp, iss, jti). > if (expectedIssuer && claims.iss !== expectedIssuer) { > throw new Error(`Invalid issuer: expected ${expectedIssuer}, got ${claims.iss}`); > } > > const nowSec = Math.floor(Date.now() / 1000); > if (claims.iat && claims.iat > nowSec + CLOCK_SKEW_SEC) { > throw new Error('Token "iat" (issued at) is in the future'); > } > if (claims.exp && nowSec - CLOCK_SKEW_SEC > claims.exp) { > throw new Error('Token has expired'); > } > if (!claims.jti) { > throw new Error('Missing "jti" (JWT ID) claim'); > } > > // --- Payload Extraction --- > // 4. Extract, decode, and parse the custom 'data' claim. > const dataB64Url = claims[CLAIM_DATA]; > if (!dataB64Url) { > throw new Error(`Missing required "${CLAIM_DATA}" claim`); > } > > const dataBuffer = Buffer.from(dataB64Url, 'base64url'); > const dataJson = dataBuffer.toString('utf8'); > > return JSON.parse(dataJson); > } > > /** > * Encrypts a base64url-encoded string payload into a nested JWS-in-JWE token. > * > * @param {string} dataB64Url - The base64url-encoded data string. > * @param {string} issuer - The 'iss' claim. > * @param {number} expiryMs - Expiry time in milliseconds. > * @returns {Promise} - The compact-serialized JWE token string. > * @private > */ > async encryptBytes(dataB64Url, issuer, expiryMs) { > this._assertInitialized(); > > try { > // --- JWS Claims Setup --- > // 1. Create the claims for the inner JWS token. > const nowMs = Date.now(); > const iat = Math.floor(nowMs / 1000); // Issued at > const exp = Math.floor((nowMs + expiryMs) / 1000); // Expiration > const jti = crypto.randomUUID(); // Unique token ID > > const claims = { > iss: issuer, > iat: iat, > exp: exp, > jti: jti, > [CLAIM_DATA]: dataB64Url, > }; > const claimsJson = JSON.stringify(claims); > > // --- JWS Creation (Signing) --- > // 2. Sign the claims with our private key. > const jwsOptions = { > format: 'compact', > fields: { > alg: SIGNATURE_ALGORITHM, > typ: 'JWT', > kid: this.privateKey.kid > } > }; > > const signedJWT = await jose.JWS.createSign(jwsOptions, this.privateKey) > .update(claimsJson) > .final(); > > // --- JWE Creation (Encryption) --- > // 3. Encrypt the signed JWS with the partner's public key. > const jweOptions = { > format: 'compact', > contentAlg: ENCRYPTION_METHOD, > fields: { > alg: ENCRYPTION_ALGORITHM, > enc: ENCRYPTION_METHOD, > kid: this.publicKey.kid, > cty: 'JWT' // Content Type: Indicates payload is a JWT > } > }; > > const encryptedJWT = await jose.JWE.createEncrypt(jweOptions, this.publicKey) > .update(signedJWT) > .final(); > > return encryptedJWT; > > } catch (error) { > throw new Error(`Nested JWT encryption failed: ${error.message}`); > } > } > > /** > * Recursively extracts all filled (non-null, non-undefined) > * leaf node paths from an object in dot-notation. > * > * @param {Object} obj - The object to traverse. > * @returns {string[]} - An array of dot-notation field paths. > * @private > */ > _extractFilledFields(obj) { > // Start the recursion with the root object and no path prefix > return this._recursiveExtract(obj, ''); > } > > /** > * Helper function for _extractFilledFields. > * @param {*} current - The current value being inspected. > * @param {string} path - The dot-notation path to this value. > * @returns {string[]} - A flat array of leaf paths found from this node. > * @private > */ > _recursiveExtract(current, path) { > // 1. Base Case: Null/Undefined values are "empty" and ignored. > if (current === null || current === undefined) { > return []; > } > > // 2. Array Case: Recurse on each item, marking the path as an array > // with a trailing "[]". This "flattens" the array, so > // 'person.addresses[0].city' and 'person.addresses[1].city' both > // map to 'person.addresses[].city'. > if (Array.isArray(current)) { > const arrayPath = path ? `${path}[]` : path; > // Use flatMap to collect and flatten results from all items. > return current.flatMap(item => this._recursiveExtract(item, arrayPath)); > } > > // 3. Object Case: Recurse on each key/value pair, *extending* the path. > if (typeof current === 'object') { > return Object.entries(current).flatMap(([key, value]) => { > const newPath = path ? `${path}.${key}` : key; > return this._recursiveExtract(value, newPath); > }); > } > > // 4. Primitive Case: This is a leaf node (string, number, boolean). > // Return its path, as long as it's not an empty path (i.e., a root primitive). > return path ? [path] : []; > } > > /** > * Throws an error if the codec is not initialized. > * @private > */ > _assertInitialized() { > if (!this.initialized || !this.privateKey || !this.publicKey) { > throw new Error('TRJWTCodec is not initialized. Call initialize() with keys first.'); > } > } > > /** > * Simple assertion helper. > * @private > */ > _assert(condition, message) { > if (!condition) { > throw new Error(message); > } > } > } > > // --- Command-line usage: encrypt --- > > async function readJsonFile(filePath) { > const content = await fs.readFile(path.resolve(filePath), 'utf8'); > return JSON.parse(content); > } > > async function main() { > const [,, publicKeyFile, privateKeyFile, ivmsDataFile] = process.argv; > > const publicKeyData = await readJsonFile(publicKeyFile); > const privateKey = await readJsonFile(privateKeyFile); > const ivmsData = await readJsonFile(ivmsDataFile); > const { issuer, publicKey } = publicKeyData; > > const codec = new TRJWTCodec(); > await codec.initialize({ publicKey, privateKey }); > > // Signs with your private key and encrypts with Sumsub's public key. > const ivmsRequest = await codec.prepareIvmsData(ivmsData, issuer); > console.log(JSON.stringify(ivmsRequest, null, 2)); > } > > main(); > ``` > > Running it (`node encrypt.js public-key.json private-key.json ivms.json`) produces: > > ```json theme={"system"} > { > "version": "IVMS101.2023", > "data": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidGVzdC1zZW5kZXItMjAyNSIsImN0eSI6IkpXVCJ9...", > "filledFields": [ > "Originator.originatorPerson[].naturalPerson.name.nameIdentifier[].primaryIdentifier", > "Originator.originatorPerson[].naturalPerson.name.nameIdentifier[].secondaryIdentifier", > "Originator.originatorPerson[].naturalPerson.name.nameIdentifier[].nameIdentifierType", > "Originator.originatorPerson[].naturalPerson.customerIdentification", > "Originator.originatorPerson[].naturalPerson.dateAndPlaceOfBirth.dateOfBirth", > "Beneficiary.beneficiaryPerson[].naturalPerson.name.nameIdentifier[].primaryIdentifier", > "Beneficiary.beneficiaryPerson[].naturalPerson.name.nameIdentifier[].secondaryIdentifier", > "Beneficiary.beneficiaryPerson[].naturalPerson.name.nameIdentifier[].nameIdentifierType", > "Beneficiary.beneficiaryPerson[].naturalPerson.customerIdentification", > "Beneficiary.beneficiaryPerson[].naturalPerson.dateAndPlaceOfBirth.dateOfBirth" > ] > } > ``` > > #### Decryption example > > To decrypt, use the same `TRJWTCodec` class with its `decryptAndDecode` method. The example input below is for illustration only — it cannot be decrypted with the keys above, since it is sample data that has already expired; test with the data Sumsub actually sends you as part of creating or reading a TRM. > > ```javascript theme={"system"} > const codec = new TRJWTCodec(); > await codec.initialize({ > publicKey, // Sumsub's public key (for signature verification) > privateKey, // Your private key (for decryption) > }); > > // Decrypts with your private key and verifies the signature with Sumsub's public key. > const decryptedIvms = await codec.decryptAndDecode(encryptedIvms.data, issuer); > console.log(JSON.stringify(decryptedIvms, null, 2)); > ``` ## Step 5: Send an outgoing transaction Create the TRM first, then the transaction. 1. **Assess the requirement**, passing the amount, asset, source, destination, and `beneficiaryVaspId`. `decision` is `REQUIRED`, `NOT_REQUIRED`, or `NEED_MORE_INFO`; when `REQUIRED`, `requiredFields` lists the IVMS101 fields to collect. 2. **Encrypt the PII** (Step 4). 3. **Create the TRM** with the same transaction details and the `ivms101` object. 4. **Create the transaction** with `travelRuleMessageId` set to the TRM's `id`, for example `trm_1234567890abcdef`. If the transaction already exists, link the TRM to it instead. In a multi-destination transaction, link a TRM to each destination, matching by `amount` and `destination`; the endpoint does not accept `destinationScreeningId`. ```ts theme={"system"} const { data: assessment } = await fireblocks.trLink.assessTRLinkTravelRuleRequirement({ customerIntegrationId, tRLinkAssessTravelRuleRequest: { amount: "1500", assetId: "ETH", direction: "OUTBOUND", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xabcd..." } }, beneficiaryVaspId: "vasp-456", }, }); if (assessment.decision === "REQUIRED") { const { data: trm } = await fireblocks.trLink.createTRLinkTrm({ customerIntegrationId, tRLinkCreateTrmRequest: { amount: "1500", assetId: "ETH", direction: "OUTBOUND", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xabcd..." } }, beneficiaryVaspId: "vasp-456", ivms101: ivms, // encrypted IVMS101 object }, }); await fireblocks.transactions.createTransaction({ transactionRequest: { assetId: "ETH", amount: "1500", source: { type: "VAULT_ACCOUNT", id: "0" }, destination: { type: "ONE_TIME_ADDRESS", oneTimeAddress: { address: "0xabcd..." } }, travelRuleMessageId: trm.id, }, }); } ``` ## Step 6: Receive an incoming transaction Create the TRM after Fireblocks detects the transaction. 1. **Assess the requirement**, passing the `txId` and the `originatorVaspId`. The transaction details are filled in from the `txId`. 2. **Encrypt the PII** you hold for your beneficiary. 3. **Create the TRM** with the `txId`, `originatorVaspId`, and `ivms101` object. 4. **Link the TRM** to the transaction. ## Resolve pending and missing messages Fireblocks notifies you of both cases in the Compliance webhook category; see [Compliance Events & Webhooks](/docs/compliance-events-webhooks) for the payloads. **A TRM needs more data** (`TRLink.RequiredActions`, once per destination). The TRM is `PENDING`. Read its required actions and resolve them one at a time; each call returns the updated TRM, which leaves `PENDING` once the last action is resolved. | Action type | What to do | | - | - | | `UPLOAD_BENEFICIARY_PII` | Encrypt the beneficiary fields listed in `data.beneficiaryRequiredFields` and submit them. | | `MANUAL_REVIEW` | A compliance review at Sumsub is needed. | **A transaction has no TRM** (`TRLink.NoTRMessage`). It waits in the Missing Travel Rule Message stage until a TRM arrives or your rule times out. To decide without waiting, for example from your own KYC data, submit a manual decision with `action` `ACCEPT` or `REJECT` and an optional `reason` of up to 500 characters, without PII. * The decision applies to every destination currently in that stage; it returns an error if there are none. * Repeating the same action has no effect; a different action replaces the earlier one. * Once a destination has moved past the stage, its decision cannot be changed. * Manual decisions are recorded with `source: "MANUAL"`. TRM statuses are `PENDING`, `ACCEPTED`, `REJECTED`, and `FAILED`. A canceled TRM cannot be reused; create a new one if the transaction goes ahead. ## Testing and monitoring Test with `BTC_TEST`, `ETH_TEST5`, and `XRP_TEST` before real value. In the Console, **Transaction History** shows the policies and rules applied to each transaction, Sumsub's responses, and the final verdict with an explanation. ## Troubleshooting Transactions not screened: check that the vault accounts are assigned to a legal entity and that the Trigger rules match. Unexpected verdicts: check the rule order and the Valid After and Valid Before windows. Integration errors such as 401 or 404 come from Sumsub; Fireblocks does not validate credentials when you connect and returns the error as is. Check that the app token and secret were created in the same Sumsub environment and application as this integration. ## Limitations * **Rules are not self-service;** Fireblocks Support applies them. * **Combining providers:** Sumsub can run alongside GTR and TRUST, but not Notabene. * **Disconnecting** permanently deletes the stored credentials and the integration, and pending TRMs may fail. Deleting a Travel Rule legal entity also deletes its integrations, TRMs, and configuration. Neither can be undone. * **Key pair:** Sumsub generates your key pair; you cannot upload your own. ## On the Help Center For Sumsub setup and monitoring in the Console, see [Sumsub](https://support.fireblocks.io/hc/en-us/articles/30614346908828-Sumsub). # Swap examples via DeFi Source: https://developers.fireblocks.com/docs/swap-via-direct-access-providers-defi This guide provides a step-by-step example of how to execute a swap using the Fireblocks Trading API via direct access providers, such as Uniswap. A swap is the process of exchanging one cryptocurrency for another on the same network. The guide demonstrates swapping ETH for USDC on Ethereum using Uniswap as the direct access provider. ## Prerequisites Before you begin, ensure you have completed the prerequisites outlined in the [Trading API Overview](/docs/trading-api-overview): * ✅ Fireblocks API credentials configured * ✅ Vault accounts with sufficient assets * ✅ Fireblocks TypeScript SDK installed (`@fireblocks/[email protected]`) ## SDK Setup First, set up your Fireblocks SDK client: ```typescript TypeScript theme={"system"} import { Fireblocks } from "@fireblocks/ts-sdk"; import * as fs from "fs"; // Initialize the Fireblocks SDK const fireblocks = new Fireblocks({ apiKey: "your-api-key", secretKey: fs.readFileSync("/path/to/your/secret.key", "utf8"), basePath: "https://api.fireblocks.io" }); ``` ## Swap Flow Overview The following steps outline the process for executing a swap via direct access providers: ```text theme={"system"} 1. List Providers 2. Approve Terms (executed once per workspace and ONLY via the Fireblocks console) 3. Create Quote 4. Execute Order 5. Monitor Order ``` ## Step 1: List Available Providers Before trading, retrieve the list of available providers and use the response to identify the `id` of your desired provider. ### API Call ```typescript theme={"system"} // List all available trading providers const providersResponse = await fireblocks.tradingBeta.getTradingProviders({ pageSize: 20 }); // Filter for direct access providers only (accountBased: false) const directAccessProviders = providersResponse.data.data.filter(provider => !provider.accountBased); ``` ### Response Example ```json theme={"system"} { "data": [ { "id": "UNISWAP_CLASSIC", "name": "Uniswap Classic", "logo": "https://example.com/logos/uniswap.png", "accountBased": false, "approved": false, "hasTermsOfService": true, "termsOfServiceUrl": "https://uniswap.org/terms" } ], "total": 1, "next": null } ``` ### Key Fields Explained | Field | Description | | - | - | | `id` | Unique identifier for the provider (use this in subsequent API calls) | | `name` | Display name of the trading platform | | `accountBased` | `false` for direct access providers, `true` for account-based providers | | `hasTermsOfService` | Whether the provider requires Terms of Service approval | | `termsOfServiceUrl` | URL to review the Terms of Service | | `approved` | For direct access providers: indicates if Terms of Service have been approved | Direct access providers may require one-time Terms of Service approval. The fields hasTermsOfService and approved indicate whether approval is required and whether your workspace already approved it. Approval must be done once per workspace via the Fireblocks Console. ### For more details about this endpoint, see the [Get Providers API Reference](/api-reference/trading/get-providers). ## Step 2: Approve Terms of Service (Direct Access Providers Only) Direct access providers require one-time Terms of Service approval before trading. **Approval must be done in the Fireblocks Console** (not available via SDK). ### Check Approval Status ```typescript theme={"system"} // Get provider details to check approval status const provider: DirectAccessProvider = providersResponse.data.data.find( p => p.id === "UNISWAP_CLASSIC" ); if (provider?.hasTermsOfService && !provider.approved) { throw new Error("Approve Terms of Service in Fireblocks Console before proceeding"); } ``` ## Clarification on how to translating user intent into baseAssetId, quoteAssetId, side, and baseAmount When creating a **Quote** or an **Order**, you express the trade using: * `baseAssetId`: the asset you **receive** on **BUY** / **give** on **SELL** * `quoteAssetId`: the counter asset used to **pay**/**receive** * `side`: * `BUY`: receive base / pay quote * `SELL`: give base / receive quote * `baseAmount`: amount in `baseAssetId` * `BUY`: base amount to receive * `SELL`: base amount to sell ### Examples | Intent | baseAssetId | quoteAssetId | side | baseAmount | | - | - | - | - | - | | Buying 100 BTC with USD | BTC | USD | BUY | 100 | | Buying 100 USD with BTC | USD | BTC | BUY | 100 | | Selling 100 BTC for USD | BTC | USD | SELL | 100 | | Selling 100 USD for BTC | USD | BTC | SELL | 100 | ## Step 3: Create a Quote for the Swap Creating a quote is required before executing a swap via direct access providers. A quote provides you with the exact exchange rate, expected amounts, fees, and price impact for the swap. ### API Call ```typescript theme={"system"} // Create a quote for swapping ETH to USDC on Ethereum with Uniswap const quoteResponse = await fireblocks.tradingBeta.createQuote({ createQuote: { scope: [{ providerId: "UNISWAP_CLASSIC" }], // Direct access provider (no accountId) baseAssetId: "ETH", // Asset you're selling quoteAssetId: "USDC", // Asset you're receiving baseAmount: "0.5", // Amount of ETH to sell side: "SELL", // Direct access provider quotes support SELL only slippageBps: 50, // Optional - Defaults to 50 BPS (0.5%) settlement: { // Required for direct access provider quotes type: "DVP", sourceAccount: { type: "VAULT_ACCOUNT", accountId: "0" }, destinationAccount: { type: "VAULT_ACCOUNT", accountId: "0" } } }, idempotencyKey: `quote-${Date.now()}` }); const quote = quoteResponse.data.quotes[0]; ``` ### Quote Response Example ```json theme={"system"} { "quotes": [ { "id": "quote_8f2e4d1a9c5b7e3f", "via": { "type": "PROVIDER", "providerId": "UNISWAP_CLASSIC" }, "type": "COMMITTED", "baseAssetId": "ETH", "quoteAssetId": "USDC", "baseAmount": "0.5", "quoteAmount": "1247.50", "quoteMinAmount": "1241.25", "priceImpact": 0.005, "expiresAt": "2024-01-15T10:35:00.000Z", "generalFees": [ { "feeType": "NETWORK", "assetId": "ETH", "amountType": "FIXED", "amount": "0.01" } ], "executionSteps": [ { "type": "APPROVE", "fee": { "feeType": "NETWORK", "assetId": "ETH", "amountType": "FIXED", "amount": "0.005" } }, { "type": "CONTRACT_CALL", "fee": { "feeType": "NETWORK", "assetId": "ETH", "amountType": "FIXED", "amount": "0.005" } } ] } ] } ``` ### Understanding Quote Fields | Field | Description | | - | - | | `id` | Unique quote identifier (use when creating order) | | `type` | Quote type: `COMMITTED` - the provider commits to the quote rates (guaranteed execution at the quoted price with respect to slippage boundaries), `INDICATIVE` - an indication of expected prices (rates may vary at execution time) | | `baseAmount` | Amount you're spending/selling | | `quoteAmount` | Expected amount you'll receive | | `quoteMinAmount` | Minimum amount guaranteed (accounting for slippage) | | `priceImpact` | Deviation from market price and execution price due to trade size or limited liquidity (as a decimal, e.g., 0.005 = 0.5%). A high price impact means your transaction will result in a substantial loss of funds | | `expiresAt` | Quote expiration time (typically 1-5 minutes) | | `generalFees` | Provider fees for the trade | | `executionSteps` | Array of steps required to complete the trade, each containing a `type` field that can be `APPROVE` (token approval), `PERMIT` (permit signature), `CONTRACT_CALL` (smart contract interaction), `EXECUTE` (main swap execution), `SETTLEMENT` (settling funds), or `DELIVERY` (delivering assets). Each step may include associated fees | > [!NOTE] > > Quotes expire quickly (typically within 1-5 minutes). Create your order promptly after receiving a quote. ### For more details about this endpoint, see the [Create Quote API Reference](/api-reference/trading/create-a-quote). ## Step 4: Execute the Swap Order Once you have a quote, execute the swap using that quote. Direct access providers like Uniswap require a quote to ensure you get the expected rate and protection against price slippage. ### Execute Order with Quote ```typescript theme={"system"} // Execute swap order using the quote from Step 3 const orderResponse = await fireblocks.tradingBeta.createOrder({ createOrderRequest: { via: { type: "PROVIDER", // Direct access provider execution providerId: "UNISWAP_CLASSIC" }, executionRequestDetails: { type: "QUOTE", quoteId: quote.id // Quote ID from Step 3 }, settlement: { type: "DVP", // Direct access swaps always use DVP sourceAccount: { type: "VAULT_ACCOUNT", accountId: "0" }, destinationAccount: { type: "VAULT_ACCOUNT", accountId: "0" } } }, idempotencyKey: `order-${Date.now()}` }); const order = orderResponse.data; ``` > [!NOTE] > > **Why direct access providers require quotes:** Unlike some account-based providers, direct access providers (like Uniswap) through Fireblocks require creating a quote first. This ensures you know the exact exchange rate, fees, and slippage protection before the transaction executes on-chain. ### For more details about this endpoint, see the [Create Order API Reference](/api-reference/trading/create-an-order). ## Step 5: Monitor Order Status After creating an order, monitor its execution status to track completion. ### Get Order Details ```typescript theme={"system"} // Get order status and details const orderStatusResponse = await fireblocks.tradingBeta.getOrder({ orderId: order.id }); console.log("Order status:", orderStatusResponse.data.status); ``` ### For more details about this endpoint, see the [Get Order Details API Reference](/api-reference/trading/get-order-details). ### List All Orders ```typescript theme={"system"} // List orders with optional filters const ordersResponse = await fireblocks.tradingBeta.getOrders({ pageSize: 20, order: "ASC", // Order by createdAt providerId: ["UNISWAP_CLASSIC"], // Filter by provider statuses: ["COMPLETED"] // Filter by status }); console.log("Orders:", ordersResponse.data.data); ``` ### For more details about this endpoint, see the [Get Orders (List) API Reference](/api-reference/trading/get-orders). ### Order Status Values | Status | Description | | - | - | | `CREATED` | Order has been created and is being processed | | `AWAITING_PAYMENT` | Waiting for payment to be received | | `PENDING_USER_ACTION` | Requires user action (e.g., approval in console) | | `PROCESSING` | Order is being executed | | `COMPLETED` | Order has been successfully completed | | `FAILED` | Order execution failed | | `CANCELED` | Order was canceled | ## Additional Resources * [Trading API Overview](/docs/trading-api-overview) * [On-Ramp, Off-Ramp, and Bridge/Swap via Account-Based Providers (CeFi)](/docs/on-ramp-off-ramp-and-bridgeswap-via-account-based-providers-cefi) - For on-ramp, off-ramp, and other account-based provider use cases * Fireblocks SDKs: [Python](https://pypi.org/project/fireblocks/) | [TypeScript](https://www.npmjs.com/package/@fireblocks/ts-sdk) | [Java](https://central.sonatype.com/artifact/com.fireblocks.sdk/fireblocks-sdk/overview) * [Fireblocks Developer Portal](/) ## Support For questions or issues with the Trading API: * Contact your Fireblocks Customer Success Manager * Email: [CSM@fireblocks.com](mailto:CSM@fireblocks.com) * Check the [Fireblocks Developer Portal](/) for updates # Sweep Funds Source: https://developers.fireblocks.com/docs/sweep-funds ## Overview Once your client, aka the end-user, deposits into an intermediary vault account, most customers will want to perform a sweeping operation. This operation consolidates all deposited funds from the intermediary vault accounts into a single treasury management vault account for various business purposes. Sweeping funds involves moving assets from address A to address B. It is important to note that this is an on-chain operation and includes **transaction fee payments**. ## Trigger the Sweeping Operation The trigger for the sweeping operation varies from customer to customer, depending on specific strategies, business needs, and regulatory requirements. For instance, some customers may choose to sweep the funds immediately after the deposit is completed. In contrast, others may trigger the sweeping mechanism only after a certain amount of deposits (either by total balance or number of deposits). Since sweeping is an on-chain operation that incurs transaction fees, some customers opt to execute the sweeping operation only when the current network fee is below a predefined acceptable threshold. Fireblocks enables customers to monitor current network fees/gas prices using the `GET /estimate_network_fee` [endpoint](/reference/estimatenetworkfee). There is no right or wrong approach; customers should determine what works best for their business needs. ### Sweep timing strategies You can sweep deposits from deposit accounts immediately after each deposit, or batch them over time. As deposits accumulate or network fees change, you can use logic-based triggers based on time, balance, or fee conditions. For example, to minimize fees, you may choose to sweep when the network is less congested or when gas prices fall below a defined threshold. If you want to sweep promptly, sweeping can occur immediately after a deposit or once Gas Station fueling is complete. ### Gas Station The Gas Station feature allows you to designate a vault account in your workspace as a Gas Station tank and keep it funded with the relevant base assets. When a deposit account receives tokens, the Gas Station fuels (transfers) base assets from the tank to the deposit account to cover fees and enable sweeping. You can configure this behavior, including when the transfer occurs and how much is transferred. ## Automate Sweeping You can automate sweeping in two ways: create sweep transactions through the API, or define automation rules in the Console that create them for you (see [Sweep with automation rules](#sweep-with-automation-rules) below). To create sweeping transactions through the API, use the `POST /transactions` [endpoint](/reference/createtransaction). Identify the vault account you want to sweep from, then send the sweep transaction request. Repeat this for every vault account you want to sweep. Automating sweeping transactions involves not only creating the transactions in a fully automated manner but also automating the entire signing process. We recommend using an [API Co-Signer](/cosigners/overview/api-cosigner-installation-flow) to automatically sweep funds according to your organization's logic and fee prices. When sweeping funds, you also pay a fee in the base asset of the deposited token's blockchain. To ensure you always have enough funds for the sweeping transaction, we introduced the [Fireblocks Gas Station](/docs/work-with-gas-station) feature, which automatically identifies incoming transactions to your predefined vault accounts and deposits enough of the base asset to cover fees for sweeping funds to your main treasury. ### Sweep with automation rules Automation rules let you sweep deposits without writing or running your own code. A rule has a trigger (when), conditions, and an action (then). When the trigger fires and the conditions are met, Fireblocks creates the sweep transaction. To cover many deposit vault accounts with one rule, target a **tag** in the rule's trigger instead of listing each vault account. The rule then applies to every vault account that carries the tag. Vault accounts you tag later are covered on their next transaction, so you don't need to edit the rule as you open new deposit accounts. #### Example: sweep deposits into an omnibus vault account 1. Create a Protected Tag (for example, `Clients`) and attach it to each deposit vault account. Each attach action requires quorum approval. 2. Create an automation rule with a transaction trigger. Set the trigger's **Destination** to the `Clients` tag. The rule runs whenever a deposit reaches any tagged vault account. 3. In the **Then** step, set the source to **Inherited Account**. This moves funds out of the vault account that triggered the rule, not a fixed account. 4. Set the destination to your omnibus vault account. You can combine tags, Wallet Pools, and individual vault accounts in the same trigger selection. For step-by-step Console instructions, see [Use tags in automation rules](https://support.fireblocks.io/hc/en-us/articles/30036020166044-Use-tags-in-automation-rules). #### Supported tags and limitations * **Protected Tags and Wallet Pools only.** Unprotected vault tags can't be used in automation rules. A tag defines which vault accounts a rule acts on, so changing that group requires the quorum approval that only Protected Tags provide. * **One sweep per deposit.** A rule runs when a deposit arrives and sweeps from that vault account. Rules don't batch deposits, run on a schedule, or check network fees. For those strategies, create sweep transactions through the API. * **Tags select the trigger, not the action.** The action still resolves to one vault account, which is what **Inherited Account** is for. The swept vault account still pays the network fee in the base asset. To keep deposit accounts funded for sweeping, use [Gas Station](/docs/work-with-gas-station) or Universal Gasless on EVM chains (see [Sweeping Tokens](#sweeping-tokens)). For more about automation rules, see [About Automation](https://support.fireblocks.io/hc/en-us/articles/14873112741660-About-Automation), [Protected Tags](https://support.fireblocks.io/hc/en-us/articles/27718237234972-Protected-Tags), and [Wallet Pools](https://support.fireblocks.io/hc/en-us/articles/27776980063260-Wallet-Pools). ## Reconciliation Processes Because memo-based wallets function as omnibus wallets, funds can be reconciled to withdrawal wallets or to cold and treasury management workspaces as needed. UTXO-based wallets also operate as omnibus wallets, allowing multiple deposited UTXOs to be reconciled to target destinations. However, transaction size limits and MPC restrictions apply: * For Bitcoin-based wallets, a single transaction is limited to 250 UTXOs. * For Cardano transactions, the overall size is capped at 16 KB. To determine the maximum spendable amount before initiating a sweep, call the [Get max spendable amount in a transaction](/reference/getmaxspendableamount) endpoint. UTXO-based chains support multiple destinations, allowing funds to be moved to different targets (for example, cold storage and withdrawal wallets) within a single transaction. For account-based chains, sweeping is done per deposit wallet. ## Fees Because sweeping is normally less time-sensitive, transaction fees can be set lower than the prevailing network fees required for immediate confirmation. ## Sweeping Tokens When sweeping tokens, the wallet should have enough native asset balance to cover the transaction fees. However, some chains support models where another account can pay the fee: * EVM chains: Universal Gasless, where a designated relay sponsors gas fees on behalf of your vault accounts. It supports sweeping all Ethereum-based tokens (ERC-20, ERC-721, and ERC-1155), but does not relay native ETH transfers. See [Fueling](/reference/sweep-to-omnibus-1#fueling) for the available relay options and the [Universal Gasless](https://support.fireblocks.io/hc/en-us/articles/19948199000092-Universal-Gasless) setup guide to configure it. * Solana: Fee payer model # Tags Source: https://developers.fireblocks.com/docs/tags Create, attach, and manage tags through the Fireblocks API, including protected tags, the quorum approval flow, and filtering vault accounts by tag. ## Overview New to tags? See the [Tags Overview](https://support.fireblocks.io/hc/en-us/articles/24809719517596) in the Help Center for a product introduction and a Console walkthrough, before following this API guide. Tags are custom labels you attach to vault accounts to classify, filter, and govern your vault inventory at scale. Two fields determine how a tag behaves, `isProtected` and `type`: * **Standard tags** (`isProtected: false`) are for organization and filtering. Every operation applies immediately. * **Protected tags** (`isProtected: true`) can be referenced in Policy rules. Editing, deleting, attaching, and detaching them all require quorum approval before they take effect. * **Wallet Pool tags** (`isProtected: true`, `type: WALLET_POOL`) are protected tags that also act as a single transaction source, routing across their member vault accounts by health. See [Wallet Pools](/docs/wallet-pools). This guide covers the full API flow: create a tag, attach it to vault accounts, filter your vault inventory by tag, and manage the tag lifecycle. It also covers the approval flow that protected tags introduce, and how to tag vault accounts at creation time. ## Requirements Tag operations are role-gated, and protected tags are gated more tightly than standard tags. | Operation | Standard tags | Protected tags | | - | - | - | | Create | Owner, Admin, Non-Signing Admin, Signer, Editor, Approver | Owner, Admin, Non-Signing Admin, Editor | | Update, delete, attach, detach | Owner, Admin, Non-Signing Admin, Signer, Editor, Approver | Owner, Admin, Non-Signing Admin, Editor | | Approve a pending operation | Not applicable | Owner, Admin, Non-Signing Admin | Initiating a protected tag operation and approving one are separate permissions. Editor can initiate any protected tag operation, but cannot approve it; the approval must come from an Owner, Admin, or Non-Signing Admin. Approvals are granted in the Fireblocks mobile app or through the API Co-signer, not through this API. Quorum requirements are configured in the Fireblocks Console under **Settings** > **Quorums** > **Approval Groups**, which provides two separate groups for protected tags: one covering edit and delete, and one covering attach and detach. See [Protected Tags](https://support.fireblocks.io/hc/en-us/articles/27718237234972) for how to configure them. ## API flow The integration follows this order: create the tag, attach it to vault accounts, then read or filter by it. ```mermaid theme={"system"} flowchart LR A[Create a tag] --> B[Attach to vault accounts] B --> C[Filter vault accounts] B --> D[Reference in Policy rules] ``` ### Step 1: Create a tag See [Create a new tag](/api-reference/tags/create-a-new-tag). `POST /v1/tags` **Request body** ```json theme={"system"} { "label": "Treasury", "description": "Vaults holding long-term reserves", "color": "#FF5733", "isProtected": true } ``` **Response** ```json theme={"system"} { "id": "9b1c...", "label": "Treasury", "description": "Vaults holding long-term reserves", "color": "#FF5733", "isProtected": true, "updatedAt": 1754827143000 } ``` **Field notes:** * `label`: required. 2 to 30 characters, alphanumeric plus spaces, hyphens, and underscores, and unique within your workspace. A duplicate label returns `409 Conflict`; a malformed one returns `400 Bad Request`. * `isProtected`: **immutable after creation.** You cannot convert a standard tag into a protected tag or the reverse. If you need a protected version of an existing tag, create a second tag and re-attach. * `type`: set to `WALLET_POOL`, together with `isProtected: true`, to create a [Wallet Pool](/docs/wallet-pools): a tag that also acts as a single transaction source with health-aware routing across its member vaults. Omit it for an ordinary tag. * `description`: optional, up to 250 characters of plain text. `color`: optional, a valid hex value. Creating a tag always takes effect immediately, including a protected tag. Approval applies only to the operations that follow. All write endpoints in this guide accept an `Idempotency-Key` header, valid for 24 hours. ### Step 2: Attach tags to vault accounts A single call attaches and detaches across many vault accounts at once. See [Attach or detach tags from vault accounts](/api-reference/vaults/attach-or-detach-tags-from-vault-accounts). `POST /v1/vault/accounts/attached_tags` **Request body** ```json theme={"system"} { "vaultAccountIds": ["12", "13", "14"], "tagIdsToAttach": ["9b1c...", "4d2f..."], "tagIdsToDetach": ["7a3e..."] } ``` The request is evaluated as a cartesian product: every tag in `tagIdsToAttach` is applied to every account in `vaultAccountIds`. Each resulting `(vaultAccountId, tagId, action)` triple is resolved independently and returned in one of three buckets. **Response** ```json theme={"system"} { "appliedOperations": [ { "vaultAccountId": "12", "tagId": "4d2f...", "action": "ATTACH" } ], "pendingOperations": [ { "vaultAccountId": "12", "tagId": "9b1c...", "action": "ATTACH", "approvalRequestId": "e81a..." } ], "rejectedOperations": [ { "vaultAccountId": "13", "tagId": "9b1c...", "action": "ATTACH", "reason": "CAPACITY_EXCEEDED" } ] } ``` | Bucket | Meaning | | - | - | | `appliedOperations` | Applied immediately. Standard tags land here. | | `pendingOperations` | Staged awaiting quorum approval, with an `approvalRequestId` to poll. Protected tags land here. | | `rejectedOperations` | Not applied and not staged, with a machine-readable `reason`. | > [!WARNING] > > A `200 OK` does not mean every operation succeeded. Partial failure is reported inside `rejectedOperations`, not through the status code. Always inspect all three buckets rather than branching on the HTTP status alone. Rejection reasons: | Reason | Cause | | - | - | | `CAPACITY_EXCEEDED` | The vault account already holds the maximum of 20 tags. | | `ATTACHMENT_ALREADY_EXISTS` | The tag is already attached to that vault account. | | `ATTACHMENT_DOES_NOT_EXIST` | A detach was requested for a tag that is not attached. | | `PENDING_REQUEST_EXISTS` | An unresolved approval request already covers that tag. | You can mix standard and protected tags in one request. The standard tag operations are applied immediately while the protected ones are staged, and the response separates them for you. The same tag ID must not appear in both `tagIdsToAttach` and `tagIdsToDetach`. ### Step 3: Filter vault accounts by tag See [Get vault accounts (paginated)](/api-reference/vaults/get-vault-accounts-paginated). `GET /v1/vault/accounts_paged?includeTagIds=9b1c...&excludeTagIds=7a3e...` **Query parameters** | Parameter | Description | | - | - | | `includeTagIds` | Return vault accounts carrying **any** of these tags. Up to 50 tag IDs. | | `excludeTagIds` | Filter out vault accounts carrying **any** of these tags. Up to 50 tag IDs. | | `tagIds` | **Deprecated.** Use `includeTagIds` instead. | Both filters use OR semantics within the list, so `includeTagIds=A&includeTagIds=B` returns accounts tagged A, B, or both. Combine the two to express "in this group but not that one", for example every `Treasury` vault that is not also `Sanctioned`. Each `VaultAccount` in the response carries a `tags` array with the full tag objects, so a filtered list is also a read of current attachments. [Get a vault account by ID](/api-reference/vaults/get-a-vault-account-by-id) returns the same `tags` array for a single account. > [!NOTE] > > Tag filtering is available on vault accounts only. `GET /v1/vault/asset_wallets` accepts no tag parameters and does not return tags, so filter at the account level and resolve wallets from there. ### Step 4: List, update, and delete tags **List tags.** See [Get list of tags](/api-reference/tags/get-list-of-tags). `GET /v1/tags` | Parameter | Description | | - | - | | `pageCursor`, `pageSize` | Cursor pagination. The response returns `next`, or `null` on the last page. | | `label` | Match tags whose label starts with this prefix. | | `tagIds` | Restrict the result to specific tag IDs, up to 100. | | `isProtected` | Return only protected or only standard tags. | | `type` | Filter by tag type, for example `WALLET_POOL`. | | `includePendingApprovalsInfo` | Populate `pendingApprovalRequest` on each tag. | To read one tag directly, use [Get a tag](/api-reference/tags/get-a-tag) at `GET /v1/tags/{tagId}`. **Update a tag.** See [Update a tag](/api-reference/tags/update-a-tag). `PATCH /v1/tags/{tagId}` Only `label`, `description`, and `color` are mutable. `isProtected` and `type` are fixed at creation. Updating a protected tag requires approval, and a tag with an unresolved approval request cannot be updated again until that request settles. **Delete a tag.** See [Delete a tag](/api-reference/tags/delete-a-tag). `DELETE /v1/tags/{tagId}` Detach the tag from every vault account first. A tag with active attachments cannot be deleted. Deleting a protected tag requires approval. ## Protected tags and the approval flow Protected tag operations are two-phase. The API call stages the change and returns an `approvalRequestId`; the change applies only once the quorum approves it in the Mobile app or Co-signer. Poll the request to follow it. See [Get an approval request by id](/api-reference/tags/get-an-approval-request-by-id). `GET /v1/tags/approval_requests/{id}` **Response** ```json theme={"system"} { "id": "e81a...", "type": "TAG_ATTACH_DETACH", "state": "PENDING" } ``` `type` is one of `TAG_UPDATE`, `TAG_DELETE`, or `TAG_ATTACH_DETACH`. `state` is one of: | State | Meaning | | - | - | | `PENDING` | Awaiting quorum approval. The change has not been applied. | | `APPROVED` | Approved and applied. | | `REJECTED` | Declined by an approver. No change was made. | | `CANCELLED` | Withdrawn before resolution. No change was made. | | `EXPIRED` | Timed out before reaching quorum. Submit a new request. | | `FAILED` | Approved but could not be applied. | While a request is `PENDING`, further operations on the same tag are rejected with `PENDING_REQUEST_EXISTS`. Treat any non-`PENDING` state as terminal and re-read the tag or the vault account to confirm the resulting state. > [!NOTE] > > This is the only approval endpoint exposed by the API. Listing pending requests, approving, rejecting, and cancelling are available in the Fireblocks Console and Mobile app only. To reverse an operation that has already been applied, submit the inverse operation, which itself requires approval. ## Tag vault accounts at creation `POST /v1/vault/accounts` does not accept tags. To create vault accounts that are tagged from the start, use the bulk endpoint with a `tagIds` array, which applies the same tags to every account it creates. See [Bulk creation of new vault accounts](/api-reference/vaults/bulk-creation-of-new-vault-accounts). `POST /v1/vault/accounts/bulk` **Request body** ```json theme={"system"} { "count": 500, "baseAssetIds": ["ETH", "BTC"], "prefix": "deposit-", "tagIds": ["9b1c..."] } ``` **Response** ```json theme={"system"} { "jobId": "3f7d...", "approvalRequestId": "e81a..." } ``` `approvalRequestId` is returned only when `tagIds` contains at least one protected tag; the accounts are still created, and the tag attachments apply once approved. Poll [Get job status of bulk creation of new vault accounts](/api-reference/vaults/get-job-status-of-bulk-creation-of-new-vault-accounts) at `GET /v1/vault/accounts/bulk/{jobId}`, which echoes `tagIds` and `approvalRequestId` alongside `status` and the created `vaultAccounts`. To create a single tagged vault account in one call, send this endpoint with `count: 1`. > [!NOTE] > > Bulk creation is in beta and is capped at 10,000 accounts per operation. HBAR, TON, SUI, TERRA, ALGO, and DOT are not supported. ## Limits | Limit | Value | | - | - | | Tags per vault account | 20, standard and protected combined | | Vault accounts per attach or detach request | 100 | | Tags to attach per request | 20 | | Tags to detach per request | 20 | | Tag IDs per `GET /v1/tags` list request | 100 | | Tag IDs per filter parameter on `accounts_paged` | 50 | Exceeding a per-request limit returns `400 Bad Request`. Exceeding the per-vault-account tag ceiling is reported per operation as `CAPACITY_EXCEEDED` in `rejectedOperations`. For label rules and field constraints, see [Tag Limits and Label Rules](https://support.fireblocks.io/hc/en-us/articles/27718268311964). ## Troubleshooting | Symptom | Cause and fix | | - | - | | `409 Conflict` when creating a tag | The label already exists in the workspace. Labels are unique per workspace. | | `400 Bad Request` when creating a tag | The label is outside 2 to 30 characters, uses disallowed characters, or `color` is not a valid hex value. | | Attach returned `200` but the tag is not on the vault account | The operation is in `pendingOperations` awaiting approval, or in `rejectedOperations`. Inspect both. | | `PENDING_REQUEST_EXISTS` | An earlier protected tag request has not settled. Poll it to a terminal state first. | | Delete fails | The tag still has vault account attachments. Detach it everywhere, then delete. | | Filtering by tag returns nothing | Confirm you are using `includeTagIds` rather than the deprecated `tagIds`, and that attachments are approved rather than pending. | ## Related * [Wallet Pools](/docs/wallet-pools): the `WALLET_POOL` tag type, which adds transaction routing on top of protected tag governance. * [Create vault accounts](/reference/create-vault-account): create the accounts you attach tags to. * [Set Policies](/docs/set-transaction-authorization-policy): reference protected tags as sources and destinations in Policy rules. * [Protected Tags](https://support.fireblocks.io/hc/en-us/articles/27718237234972): quorum configuration and Policy patterns. # Tokenization Source: https://developers.fireblocks.com/docs/tokenization Deploy, manage, mint, and burn custom tokenized on-chain assets securely using the Fireblocks API # What is Tokenization? The Fireblocks asset tokenization platform is a secure and compliant way to digitalize assets and build blockchain-based applications. Our REST API provides powerful capabilities for automating tasks like creating, redeeming, managing, and issuing tokens, such as stablecoins or security tokens. [Start developing on Fireblocks today](https://www.fireblocks.com/developer-sandbox-sign-up/?gl=1*19mlzew*_ga*MTY4ODc5Mjc3Ni\[%E2%80%A6]_ga_D39L1D2ZX2*MTY4Nzg2MTQwOS4xNi4xLjE2ODc4Njc3OTMuMjcuMC4w). # Launch your tokenization project with Fireblocks API You can use our Fireblocks API for tokenization in different ways: * Tokenize any asset, including fiat currencies, securities, and other illiquid assets * Mint new tokens * Manage smart contracts and whitelisting approval * Execute any smart contract function * Secure custody of your tokens * Trade your tokens on exchanges or through peer-to-peer (P2P) transactions * Burn tokens that are no longer needed to manage the token supply * Integrate with other DeFi applications With the Fireblocks asset tokenization platform, you can use our API to automate minting and token issuance 24/7. You can also manage your daily token operations using our easy-to-use dashboard. Learn about [our turnkey solution](https://www.fireblocks.com/platforms/tokenization/). # Guides ## Raw Message Signing Support more chains and actions. Generate ECDSA and EdDSA signatures to [sign any transaction type or message](/docs/raw-signing). ## Typed Message Signing Allows you to [sign messages using specific standard formats](/docs/typed-message-signing-1) that prefix the message with a magic string. For ETH\_MESSAGE and EIP712 messages. ## Creating Vaults and Wallets [Generate Fireblocks vault accounts and asset wallets](/docs/create-direct-custody-wallets) at scale for both account-based and UTXO asset wallet types using the Fireblocks API and SDKs. ## Whitelisting External Wallets Use API calls and endpoints that [whitelist external wallet addresses](/docs/whitelist-addresses) so that you can send transactions from wallets that exist outside your Fireblocks Vault. ## Minting an NFT [Mint and verify Non-Fungible Tokens (NFT)](/reference/mint-an-nft) for your NFT collection using the Fireblocks Hardhat Plugin or Web3 Provider. ## Ethereum Smart Contract Development Smart contract development frameworks to [develop, test, and deploy smart contracts on EVM-based blockchains](/reference/hardhat-plugin) easily. Four different ways to deploy your contracts. # Developer Community Want to learn more from Fireblocks knowledge experts and other developers? [Join our developer community today](https://community.fireblocks.com/)! # Overview Source: https://developers.fireblocks.com/docs/trading-api-overview ## Introduction The Fireblocks Trading API enables several use cases related to liquidity and conversions, executing trades directly from your Fireblocks workspace, integrating dApps and centralized providers. With it, you can create Orders for the following use cases: * Swap one asset to another on decentralized and centralized providers, over the same network. For example, swap USDC over Ethereum to USDT over Ethereum from one of your vaults via dApps like Uniswap or connected accounts like Scrypt * On/off ramp via connected accounts with one of the providers. For example, on-ramp MXN to USDC over Polygon via your connected account with Alfred Pay * Bridge assets between different chains via account-based providers (see [On-Ramp, Off-Ramp, and Bridge/Swap via Account-Based Providers (CeFi)](/docs/on-ramp-off-ramp-and-bridgeswap-via-account-based-providers-cefi)) **Additional use cases:** * Convert one asset to another within your connected account with account-based providers - to be added in the near future This guide outlines the process for executing Orders using the Fireblocks API, including prerequisites, a detailed workflow, and necessary code examples for successful operations utilizing the different use cases mentioned above. ## Core Terminology | Term | Description | | - | - | | **Order** | An instruction created through the trading API that defines what asset to buy or sell, at what price (market or quote), and how to settle it (DVP or prefunded). Each order has a unique ID and a lifecycle status that remains until it is completed or fails. | | **Quote** | A temporary price offer from a provider that defines the exchange rate and expiry time. Quotes can be Indicative (for price preview only) or Committed (locked and executable until expiration). | | **Provider** | External trading provider or exchange venue. For example, Bridge or AlfredPay for account-based providers, and Uniswap for direct access providers are commonly used in DeFi. | | **Settlement Type** | Defines how and when funds move during a trade.- **DVP**: The provider waits for the user's fund deposit before executing the order, ensuring delivery occurs only after payment is received.- **Prefunded**: Executes instantly using the user's existing balance already held with the provider. | | **Execution Type** | Defines how the order price is determined.- **Market**: Executes immediately at the best available rate from the provider.- **Quote**: Executes at a locked rate from a committed quote created beforehand using the Quote endpoint. | | **Participants Identification** | Defines the originator and beneficiary involved in an order. Used to determine who initiates and who receives the transaction, and what level of PII data is required by the provider (e.g., first-party vs third-party). | ## Understanding Trading Providers The Fireblocks Trading API supports two types of providers: ### Direct Access Providers DeFi platforms that provide direct access to decentralized protocols without requiring account management, such as Uniswap. **Key Characteristics:** * **Access Type**: Direct - no account credentials needed * **Approval**: One-time Terms of Service acceptance ### Account-Based Providers Centralized providers that require users to open an account with them. Account-based access requires credentials (API keys). **Key Characteristics:** * **Access Type**: Account-based - customers need to open an account with the provider before they can use the API to create Orders via Fireblocks. After creating the account, the customer needs to generate API keys within their account in the provider console, and then connect the account via the connected-account page in the Fireblocks console. For detailed instructions, see [Fireblocks Connected Accounts](https://support.fireblocks.io/hc/en-us/articles/27221452097692-Connected-accounts-and-balances) ## Prerequisites ### 1. Fireblocks API Credentials * Obtain API and Secret Keys from Fireblocks. * Ensure the credentials have sufficient permissions for trading operations. ### 2. SDK Installation Install or update the Fireblocks SDK with trading support before proceeding. ### 3. Other Requirements Different providers have specific requirements: **For direct access providers:** * Trades execute on-chain, requiring gas fees in the native blockchain asset. For example, Ethereum-based swap orders via Uniswap will require you to have ETH for gas * The execution of direct access orders is done via smart contracts. Ensure you have policy rules that allow smart contract calls within your policy **For account-based orders:** * Minimum trade amounts vary by platform * Execution and settlement are handled through connected accounts * Ensure you have both Orders and Transfer policy rules inside your policy ## High-Level Trading Flow ```text theme={"system"} 1. List Providers │ ├─→ GET /providers │ └─→ Returns available providers │ 2. Approve Terms (For direct access only) │ ├─→ One-time approval for direct access providers - Approved only from the console │ 3. Create Quote (optional - some providers allow creating Market orders without creating a quote beforehand) │ ├─→ POST /trading/quotes │ └─→ Get exchange rate, amounts, and fees │ 4. Create Order │ ├─→ POST /trading/orders │ ├─→ Using quote (quote-based execution) │ └─→ Direct market order │ 5. Monitor Order │ └─→ GET /trading/orders/{orderId} └─→ Track execution status and completion ``` ## Next Steps See the following guides for implementation examples: * [Swap via Direct Access Providers (DeFi)](/docs/swap-via-direct-access-providers-defi) - Step-by-step guide for executing swaps using direct access providers like Uniswap * [On-Ramp, Off-Ramp, and Bridge/Swap via Account-Based Providers (CeFi)](/docs/on-ramp-off-ramp-and-bridgeswap-via-account-based-providers-cefi) - Step-by-step guide for on-ramp, off-ramp, and swap operations using account-based providers # Transaction Screening Operations Source: https://developers.fireblocks.com/docs/transaction-screening-operations Read a screened transaction's full compliance result, and bypass, rescreen, freeze, or unfreeze it through the Fireblocks API. These operations apply to one transaction, any time after screening starts. To change what happens to future transactions, update your policy instead; see [Compliance Policies](/docs/compliance-policies). ## Transaction states | State | API | What you can do | | - | - | - | | Screening | `status` is `PENDING_AML_SCREENING` | Wait, or rescreen if it takes longer than expected. | | Accepted (Accept or Alert) | `status` moves past `PENDING_AML_SCREENING`; the screening result's `status` is `Completed` | Nothing required. Read the result for details. | | Rejected (outgoing) | `status` is `REJECTED`, with a screening `subStatus` such as `REJECTED_AML_SCREENING` | Bypass it if the block was a false positive. | | Frozen (incoming) | `subStatus` is `AUTO_FREEZE` or `FROZEN_MANUALLY` | Unfreeze it once you have reviewed the case. | A Reject or Freeze outcome on an outgoing transaction rejects it; nothing is frozen, because no funds moved. A transaction can stay in `PENDING_AML_SCREENING` because of network confirmation delays between Fireblocks and your provider; this usually resolves on its own. See [Sub-statuses](/reference/sub-statuses) for every `subStatus` value. For several destinations, the most restrictive result decides; see Compliance Policies. ## What you can do through the API | Task | Applies to | Permission | Console | API | | - | - | - | - | - | | Read the result | Any screened transaction | All roles | Transaction History > Transaction Details | [`GET /screening/transaction/{txId}`](/api-reference/compliance/provides-all-the-compliance-details-for-the-given-screened-transaction) | | Bypass | Outgoing transactions only, after screening has rejected or failed them. | Owner, Admin | Transaction History > Transaction Details | [`POST /screening/transaction/{txId}/bypass_screening_policy`](/api-reference/compliance/bypass-screening-policy) | | Rescreen | Incoming transactions only, after screening rejected or failed them; returns the transaction to PENDING\_AML\_SCREENING. Optionally clears the linked Travel Rule message. Not available for frozen transactions. | All roles | Transaction History > Transaction Details | [`POST /screening/transaction/{txId}/rescreen`](/api-reference/compliance/rescreen-a-rejected-transaction) | | Freeze | Incoming transactions | Admin, Non-Signing Admin | No | [`POST /transactions/{txId}/freeze`](/api-reference/transactions/freeze-a-transaction) | | Unfreeze | Frozen transactions | Owner, Admin, Non-Signing Admin | Transaction History > Transaction Details | [`POST /transactions/{txId}/unfreeze`](/api-reference/transactions/unfreeze-a-transaction) | * **Bypass** creates a new transaction, with the API user as initiator, that skips screening. The original stays rejected. * **Rescreen** returns the transaction to pending screening. Set `resetTravelRuleMessage` to `true` to discard the existing Travel Rule message. * **Freeze** makes the funds unspendable: the full amount for account-based assets, all the transaction's UTXOs for UTXO-based assets. The four actions accept an optional `Idempotency-Key` header, valid for 24 hours. Permissions are workspace defaults: bypass and unfreeze can be blocked workspace-wide, after which they require a Fireblocks Support ticket. See [Workspace-wide limits](/docs/compliance-policies#workspace-wide-limits). ## Read the result ```ts theme={"system"} const { data } = await fireblocks.compliance.getScreeningFullDetails({ txId }); console.log(data.status, data.aml?.verdict, data.aml?.risk); ``` | Field | Description | | - | - | | `aml` | The final AML/KYT result. | | `tr` | The Travel Rule result. | | `amlList` | Every AML/KYT result recorded, including those before a rescreen. | | `status` | Screening progress across checks, for example `AMLStarted`, `AMLCompleted`, `TRStarted`, `TRCompleted`, `Completed`. | Each result in `aml`, `tr`, and `amlList` includes: | Field | Description | | - | - | | `provider` | The provider that ran the check. | | `verdict` | The provider's verdict, for example `PASS`. | | `risk` | Provider-specific risk level, for example `lowRisk`. | | `screeningStatus` | Status of this check, for example `COMPLETED`. A check can finish while the transaction is still in later checks. | | `bypassReason` | Why screening was skipped, for example `PASSED_BY_POLICY` or `UNSUPPORTED_ASSET`. | | `customerRefId` | The Customer Reference ID the result is attributed to. | | `payload` | The provider's raw response; structure differs by provider. | To be notified instead of polling, see [Compliance Events & Webhooks](/docs/compliance-events-webhooks). ## Limitations * `GET /screening/transaction/{txId}` does not include Address Registry Screening results. ## On the Help Center To read a result or act on a transaction in the Console, see [Transaction Screening Operations](https://support.fireblocks.io/hc/en-us/articles/30614325361948-Transaction-Screening-Operations). # Travel Rule Overview Source: https://developers.fireblocks.com/docs/travel-rule-overview Compare the Travel Rule providers Fireblocks supports, and see what you can do with each through the API or the Console. The Travel Rule requires Virtual Asset Service Providers (VASPs) to share originator and beneficiary information for qualifying transactions. With Notabene, Sumsub, or GTR, Fireblocks screens the transaction against your Travel Rule policy and the provider sends the data to the counterparty VASP; TRUST lets you prove that you own your addresses. Available on Developer Sandbox, Testnet, and Mainnet workspaces. Transfers to connected exchanges and on/off-ramp orders do not use these providers; see [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule) and [On/Off-Ramps](/docs/on-off-ramps-travel-rule). ## Providers | Provider | How you integrate | Enablement and connection | | - | - | - | | [Notabene](/docs/notabene) | Validate and encrypt the Travel Rule data, then attach it to the outgoing transaction. | Fireblocks Support enables it; you connect it in the Console. | | [Sumsub](/docs/sumsub) | Create an encrypted Travel Rule message, then link it to the transaction. | Fireblocks Support enables it; you connect it through the API, with Sumsub auto-connect. | | [GTR](/docs/gtr) | Create an encrypted Travel Rule message, then link it to the transaction. | Fireblocks Support enables it; you connect it through the API with your GTR API key and secret. | | [TRUST](/docs/interact-with-trust) | Sign a proof of address from a vault account, then submit it to TRUST. | Free; no enablement needed. | ## What you can do through the API | Task | Console | API | Guide | | - | - | - | - | | Create and attach Travel Rule messages | No | Yes | Provider guide | | Connect Sumsub or GTR | No | Yes | [Sumsub](/docs/sumsub), [GTR](/docs/gtr) | | Disconnect Sumsub or GTR | Through Fireblocks Support | Yes | [Sumsub](/docs/sumsub), [GTR](/docs/gtr) | | Respond to a missing or incomplete message (Sumsub, GTR) | No | Yes | [Compliance Events & Webhooks](/docs/compliance-events-webhooks) | | Publish a proof of address | No | Yes | [TRUST](/docs/interact-with-trust) | | Connect Notabene | Yes | No | [Notabene](/docs/notabene) | | Read the Travel Rule policy | Yes | Yes | [Compliance Policies](/docs/compliance-policies) | | Configure outage and delay behavior | Yes | Yes | [Compliance Policies](/docs/compliance-policies) | | Read a transaction's Travel Rule result | Yes | Yes, in the `tr` field | [Transaction Screening Operations](/docs/transaction-screening-operations) | ## Limitations * **Combining providers:** Sumsub and GTR can be active on the same workspace; Notabene cannot run alongside Sumsub or GTR. TRUST works alongside any provider. * **Encryption:** with Notabene, Sumsub, and GTR you encrypt the PII on your side, so Fireblocks never holds the keys to decrypt it. * **TRUST has no screening policy;** it covers proof of address only. ## On the Help Center To connect Notabene or build Travel Rule rules in the Console, see [About Travel Rule](https://support.fireblocks.io/hc/en-us/articles/30614346859292-About-Travel-Rule). # Treasury Management Source: https://developers.fireblocks.com/docs/treasury-management Store your funds securely in hot and cold wallets, manage your fund operations, and connect to exchanges, DeFi, and trading counterparties with Fireblocks Treasury Management # What is Treasury Management? Fireblocks Treasury Management helps maintain a holistic view of your firms’ assets across wallets, connected venues, and fiat providers with a view to managing operational risk and liquidity. [Start developing on Fireblocks today](https://www.fireblocks.com/developer-sandbox-sign-up/?gl=1*19mlzew*_ga*MTY4ODc5Mjc3Ni\[%E2%80%A6]_ga_D39L1D2ZX2*MTY4Nzg2MTQwOS4xNi4xLjE2ODc4Njc3OTMuMjcuMC4w). # Set up, maintain, and scale your fund operations through Fireblocks Treasury Management Using Fireblocks Treasury Management, you can streamline the functionality of your operations, enabling you to grow and scale securely: * **Protect fund operations:** Safely store tokens and NFTs and securely access exchanges or fiat accounts with our multi-layer security. * **Set up governance rules:** Use transaction authorization policy rules to create automated preferences catered around specific business requirements. Configure different roles and approval levels for stakeholders, and automate approval workflows to save time and maintain efficiency. * **Operationalize treasury management:** Use our services to view all treasury assets across hot or cold wallets and track and report the movement of internal funds. * **Easily move assets around:** Use Treasury Funds in DeFi, Staking, or Tokenized Funds. Use Gas Station or gasless transactions to manage gas as you move assets. * **Make secure transfers:** Connect with counterparties across the Fireblocks Network and transact in a secure channel taking advantage of address resolution while preventing human errors or man-in-the-middle attacks. * **Trading:** Automate, rebalance, and transfer between exchange accounts while minimizing counterparty risk to exchanges using our treasury management offering. Learn more about [our Treasury Management](https://www.fireblocks.com/blog/the-4-pillars-of-great-treasury-management-in-web3/). # Guides ## Raw Message Signing Support more chains and actions. Generate ECDSA and EdDSA signatures to [sign any transaction type or message](/docs/raw-signing). ## Typed Message Signing Allows you to [sign messages using specific standard formats](/docs/typed-message-signing-1) that prefix the message with a magic string. For ETH\_MESSAGE and EIP712 messages. ## Creating Vaults and Wallets [Generate Fireblocks vault accounts and asset wallets](/docs/create-direct-custody-wallets) at scale for account-based and UTXO asset wallet types using the Fireblocks API and SDKs. ## Whitelisting External Wallets Use API calls and endpoints that [whitelist external wallet addresses](/docs/whitelist-addresses) so that you can send transactions from wallet types using the Fireblocks API and SDKs. ## Gas Station Setup and Usage [Auto-fuel a vault account to fund transaction fees](/docs/enabling-the-gas-station-1) with the base asset for EVM-based blockchains to automate and simplify your omnibus vault operations. ## Gasless Transactions [Send tokens without holding the native asset](/docs/gasless-transactions) in the source vault account, with a relay vault account that pays the fee. See [Gas Management](/docs/gas-management) to compare gasless transactions with Gas Station. ## Creating a Transaction Use transaction API calls to [initiate any Fireblocks transaction operation](/transactions/create-transactions), such as transfer, mint, burn, contract call, raw, and more. ## Monitoring Transaction Statuses Set up automated webhook notifications to [monitor the progression of your transactions](/reference/monitoring-transaction-status) via transaction statuses and sub-status messages. ## Exchange Transfers Use the `Create a new transaction` endpoint call to [manage external transfers to and from your linked exchange accounts](/docs/connect-to-exchanges-and-fiat-providers). # Developer Community Want to learn more from Fireblocks knowledge experts and other developers? [Join our developer community today](https://community.fireblocks.com/)! # TRM Labs Source: https://developers.fireblocks.com/docs/trm-labs Connect TRM Labs for AML/KYT screening: asynchronous results and the Wait action, default timeout verdicts, and supported assets. TRM Labs is an AML/KYT provider in Early Access. It is a premium add-on, purchased separately from Early Access enrollment, available on Mainnet workspaces; contact your Customer Success Manager to join. You connect it in the Console; there is no API for connecting. Through the API you read its policy and results the same way as for any AML/KYT provider; see [AML/KYT Overview](/docs/aml-kyt-overview). ## What is sent and returned Fireblocks sends the chain, asset, amount and USD value, destination address, transaction hash, Fireblocks transaction reference, and the [Customer Reference ID](/docs/customer-reference-id). TRM Labs returns a categorical risk level and the risk categories it matched, if any. | Direction | Screened through | | - | - | | Incoming | TRM Labs transaction monitoring | | Outgoing | TRM Labs wallet screening | Outcome (Post-Screening Policy) actions: Accept, Reject, Alert, Wait. ## Asynchronous results and Wait TRM Labs results are not always immediate. The Wait action holds a transaction until a new result arrives or a window you define closes, set with two Outcome fields: | Field | Meaning | | - | - | | Valid After | Minimum time before the rule applies | | Valid Before | Maximum time within which the rule applies | ## Connect You need a TRM Labs API key with Know Your Transaction (KYT) API access. Enter it in the Console. The default Trigger (Screening Policy) and Outcome rules cannot be edited or removed; custom rules you add above them take precedence. To switch to new credentials, submit a ticket to Fireblocks Support. ## Timeouts Timeout windows are measured in hours, because TRM Labs monitors the transaction and polls for alerts instead of resolving it at once. If no result arrives before the window closes, Fireblocks applies a default verdict unless your policy says otherwise: | Direction | Default verdict | | - | - | | Incoming | Accept, since the funds have already arrived | | Outgoing | Reject | `bypassScreeningDuringServiceOutages` does not apply to TRM Labs; its timeout behavior comes only from these defaults and your policy rules. ## Supported assets TRM Labs supports a broad range of blockchains, including Bitcoin, Ethereum, Solana, and TRON. ## Limitations * Outgoing transactions are not registered with TRM Labs transaction monitoring during Early Access. * Mainnet only; Testnet assets are not supported. * Rejection details are available only from Fireblocks, not from TRM Labs. ## On the Help Center To connect TRM Labs or test your rules in the Console, see [TRM Labs](https://support.fireblocks.io/hc/en-us/articles/30614328569756-TRM-Labs). # TrustCo Travel Rule Source: https://developers.fireblocks.com/docs/trustco-travel-rule The Travel Rule `piiData` fields TrustCo requires for withdrawals, first party and third party, with an example. TrustCo requires Travel Rule `piiData` for withdrawals. The same fields apply to first-party and third-party withdrawals. TrustCo is a custodian, not an exchange, and is unrelated to [TRUST](/docs/interact-with-trust), the Travel Rule network. For how to build, encrypt, and send the payload, see [Exchanges Travel Rule Overview](/docs/exchanges-travel-rule). ## Required fields for withdrawals | Path | Values | Required when | | - | - | - | | `data.beneficiary.participantRelationshipType` | `FirstParty`, `ThirdParty` | Always | | `data.beneficiary.entityType` | `Individual`, `Business` | Always | | `data.beneficiary.names[0].primaryName` | Free text, with `nameType` `Latin` | `Individual` | | `data.beneficiary.names[0].secondaryName` | Free text | `Individual` | | `data.beneficiary.company.name` | Free text | `Business` | | `data.beneficiary.postalAddress.streetName` | Free text | Always | | `data.beneficiary.postalAddress.buildingNumber` | Free text | Always | | `data.beneficiary.postalAddress.city` | Free text | Always | | `data.beneficiary.postalAddress.postalCode` | Free text | Always | | `data.beneficiary.postalAddress.country` | Free text | Always | | `data.beneficiaryVASP.vaspName` | Free text: the receiving financial institution | | | `data.beneficiary.externalReferenceId` | Free text: the account number | | Identify the beneficiary institution with `beneficiaryVASP.vaspName`, not `vaspCode`. ## Example ```json theme={"system"} { "type": "exchange-service-travel-rule", "typeVersion": "1.0.0", "data": { "beneficiary": { "participantRelationshipType": "FirstParty", "entityType": "Business", "company": { "name": "ACME Inc" }, "postalAddress": { "streetName": "Oak street", "buildingNumber": "1", "city": "Boston", "postalCode": "02001", "country": "US" }, "externalReferenceId": "111-222-333-444" }, "beneficiaryVASP": { "vaspName": "Prosperity Financial" } } } ``` ## On the Help Center To provide TrustCo Travel Rule data in the Console, see [TrustCo travel rule](https://support.fireblocks.io/hc/en-us/articles/30750192475036-TrustCo-travel-rule). # Sign Typed Messages Source: https://developers.fireblocks.com/docs/typed-message-signing-1 ## What is Typed Message Signing? Typed Message Signing is a crucial function in the blockchain ecosystem, designed to ensure security, transparency, and user trust when interacting with various blockchain applications. Typed Message Signing refers to the process of creating and signing structured data that both machines and humans can understand and verify. Unlike traditional message signing that deals with arbitrary data, Typed Message Signing structures the data according to predefined formats, making it readable and understandable before it is signed. This process ensures that the signer fully comprehends what they are agreeing to, thereby enhancing the security and clarity of blockchain transactions. ## What are the use cases for Typed Message Signing? * **Proof of Ownership:** Users can sign messages to demonstrate ownership of a particular blockchain address or asset without making any on-chain transaction, crucial for identity verification processes. * **Proof of Reserves:** Financial institutions can use typed message signing to prove possession of sufficient funds or assets transparently, enhancing trust among users or regulators. * **Compliance with Regulations:** In scenarios like the Travel Rule and [TRUST Platform](https://www.coinbase.com/blog/introducing-the-travel-rule-universal-solution-technology-trust) in the cryptocurrency space, businesses must share certain transaction details with regulatory bodies. Typed message signing ensures that this information is exchanged securely and verifiably. * **Smart Contracts and Legal Agreements:** In complex agreements or smart contracts, typed message signing can be used to confirm terms, conditions, and any other contractual obligations clearly and securely. * **Meta-Transactions:** Typed message signing enables meta-transactions, where users sign transactions that someone else submits to the network. This is particularly useful in dApps that aim to reduce the gas cost burden on users. * **Authentication:** Users can sign a typed message to prove ownership of a blockchain address without making a transaction. This method can be used for logging into decentralized applications (dApps) securely. ## How does Typed Message Signing work? * **Data Structuring:** Initially, the data to be signed is structured into a predefined format. This structure includes defining types, fields, and the order in which the fields appear. This data format is often defined in a smart contract or a protocol specification. * **User Confirmation:** Before signing, the structured data is presented to the user in a human-readable form. This step is vital as it allows the user to verify the details of the data they are about to sign, ensuring transparency and informed consent. * **Signing:** The user signs the data with their private key. This signature process involves cryptographic algorithms that securely associate the signer's identity with the data they approved. * **Verification:** The signature can then be verified by anyone who has access to the signer’s public key, confirming that the specific data was signed by the owner of the private key and that it has not been tampered with since signing. ## Typed Message Signing in Fireblocks ### How to enable Typed Message Signing To sign a Typed Message, [create a Typed Message Policy rule](https://support.fireblocks.io/hc/en-us/articles/29214070429468-Typed-Message-policy-rules#h_01KYS7A7105NFG84PGP6WRD5C5). ### Supported assets and methods Typed message signing in Fireblocks is supported for several assets and can be implemented through various methods: **Supported Assets:** * [EVM-based blockchains (EIP191 & EIP712)](/reference/sign-typed-messages-for-ethereum-and-evm-networks) * [Bitcoin](/reference/signing-typed-messages-in-bitcoin) * [Tron (TIP191)](/reference/signing-typed-messages-in-tron) **Creation Methods for Typed Message Signing Requests:** * By decentralized applications (dApps) using the [Fireblocks Browser Extension](https://support.fireblocks.io/hc/en-us/articles/5403962226332-Connecting-to-Web3-using-the-Fireblocks-Chrome-Extension) or [Wallet Connect integration](https://support.fireblocks.io/hc/en-us/articles/5403817784732-Connecting-to-Web3-using-WalletConnect) * Through the Fireblocks API, using the [Create Transaction](/reference/createtransaction) API call ### Via the Browser Extension or Wallet Connect Typed Message Signing requests are created by Decentralized Applications (dApps) when connected via Fireblocks Browser Extension or Wallet Connect integration, for various purposes such as (but not limited to): * **Session Authentication:** Similar to traditional web applications, dApps can use message signing to manage sessions. Once a user signs a message to prove ownership of an address, the dApp can create a session for the user without requiring them to sign in through traditional methods like username and password * **Proving Ownership:** dApps often need to verify that a user controls a specific blockchain address. Typed message signing enables users to prove ownership without having to conduct a transaction, which can save on transaction fees * **Meta-transactions:** Users can sign transactions that are paid for and submitted by another party (like a dApp). This can be used to improve user experience by allowing users to interact with the blockchain without needing to spend Ether for gas ![](https://mintcdn.com/fireblocks-43c4b3ee/8uV7V_rBjqF0ZDAg/images/docs/3232ff3-03.jpg?fit=max\&auto=format\&n=8uV7V_rBjqF0ZDAg\&q=85\&s=89ef1698c0dded3afeb2bf8d206937e6) EIP712, EIP191, and dApp authentication signing request examples ### Via the Fireblocks API Typed Message Signing requests can also be initiated through the API by using the Create Transaction API. These requests are typically generated for scenarios such as (but not limited to): * **Proof of Ownership:** Users can sign messages to demonstrate ownership of a particular blockchain address or asset without making any on-chain transaction, crucial for identity verification processes * **Compliance with Regulations:** In scenarios like the Travel Rule and [TRUST Platform](https://www.coinbase.com/blog/introducing-the-travel-rule-universal-solution-technology-trust) in the cryptocurrency space, businesses must share certain transaction details and regulatory bodies. Typed message signing ensures that this information is exchanged securely and verifiably * **Proof of Reserves:** Financial institutions can use typed message signing to prove possession of sufficient funds or assets transparently, enhancing trust among users or regulators > [!NOTE] > > Learn more on Typed Message Signing via API in the following [guide](/reference/typed-message-signing-2)! To build the request itself, see [Create typed message transactions](/transactions/typed-messages), which shows the SDK, API and CLI examples for each supported message type. # USDC Gateway Source: https://developers.fireblocks.com/docs/usdc-gateway ## Overview New to USDC Gateway? See the [USDC Gateway Overview](https://support.fireblocks.io/hc/en-us/articles/27419996238620-USDC-Gateway-Overview) for a product introduction to the Circle Gateway integration and a console overview, before following this API guide. USDC Gateway connects your Fireblocks vault accounts to [Circle Gateway](https://developers.circle.com/gateway), a cross-chain liquidity layer for USDC. Each vault account can have one Gateway wallet that holds a unified USDC balance across supported chains. You deposit USDC from a vault asset wallet into Gateway, then withdraw to any supported chain without managing per-chain inventory yourself. Deposits can also be put on a recurring schedule instead of submitted manually — see [Automate deposits](#automate-deposits). This guide covers the full API flow: activate a Gateway wallet, deposit USDC, check balance, withdraw USDC, and archive the wallet. Deposits and withdrawals use the standard [Create a new transaction](/reference/createtransaction) endpoint with `subType: VIRTUAL_ACCOUNT` on the source or destination. ## Requirements See [Prerequisites](https://support.fireblocks.io/hc/en-us/articles/27420202963612-USDC-Gateway-Prerequisites-and-Setup-Guide#h_01KREPP3DYFG5A4QJEZ8CKY836) in the USDC Gateway Setup Guide for full workspace requirements, including policy rules, vault account setup, USDC asset wallets, and native gas balance. ## API flow The integration follows this order: Activate → Deposit → Check balance → Withdraw. ```mermaid theme={"system"} flowchart LR A[Activate Gateway wallet] --> B[Deposit USDC] B --> C[Check Gateway balance] C --> D[Withdraw USDC] ``` ### Step 1: Activate the Gateway wallet Activation creates the Gateway wallet bound to the vault account. No funds move. See [Activate a USDC Gateway wallet](/api-reference/vaults/activate-a-usdc-gateway-wallet). **Request body** ```json theme={"system"} { "vaultAccountId": "1267" } ``` **Response** ```json theme={"system"} { "walletId": "9b...", "status": "ACTIVATED" } ``` Re-run the activate endpoint on an archived wallet to reactivate it. ### Step 2: Deposit USDC into Gateway Submit a deposit using the [Create a new transaction](/reference/createtransaction) endpoint. Set `subType: VIRTUAL_ACCOUNT` on the **destination** to route funds into the Gateway wallet instead of a standard asset wallet. `POST /v1/transactions` **Request body** ```json theme={"system"} { "assetId": "USDC_ETH_TEST5", "amount": "10", "source": { "type": "VAULT_ACCOUNT", "id": "0" }, "destination": { "type": "VAULT_ACCOUNT", "id": "0", "subType": "VIRTUAL_ACCOUNT" } } ``` **Field notes:** * `assetId`: the USDC asset ID for the source chain. For the current list of Gateway-supported chains, see the [Gateway supported blockchains](https://support.fireblocks.io/hc/en-us/articles/27420202963612-USDC-Gateway-Prerequisites-and-Setup-Guide#h_01KTM72K9G6VQ9J2DN3SQHMAQW). * `source.id` and `destination.id`: the same vault account ID that hosts the Gateway wallet. * `destination.subType`: must be `VIRTUAL_ACCOUNT` for Gateway deposits. The deposit reaches `COMPLETED` once your workspace's transaction confirmation policy is satisfied — either a policy you've defined, or the Fireblocks default if you haven't set one. Track status via the [Transactions API](/api-reference/transactions/get-a-specific-transaction-by-fireblocks-transaction-id) or [transaction webhooks](/reference/monitoring-transaction-status). > [!NOTE] > > Because completion is driven by your confirmation policy rather than by Circle Gateway's own balance-credit confirmation, a deposit can occasionally show as `COMPLETED` slightly before on-chain finality and the Gateway balance update are fully reflected. The first deposit from a vault address on a given chain triggers an automatic `APPROVE` transaction for Gateway smart contract approval. If your workspace Policies do not already cover this, add an `APPROVE` rule (see [Setting up policy rules for USDC Gateway](https://support.fireblocks.io/hc/en-us/articles/27420202963612-USDC-Gateway-Prerequisites-and-Setup-Guide)) before initiating your first deposit on each chain. Once approved, subsequent deposits on the same chain proceed without an additional approval step. ### Step 3: Check Gateway balance Retrieve your total balance and per-chain breakdown. See [Get USDC Gateway wallet info](/api-reference/vaults/get-usdc-gateway-wallet-info). **Response fields** | Field | Description | | - | - | | `status` | `ACTIVATED` or `DEACTIVATED` | | `symbol` | Always `USDC` | | `totalBalance` | Total USDC balance across all supported chains | | `balanceBreakdown` | Per-chain breakdown showing how funds are distributed | | `assetIds` | Fireblocks asset IDs covered by this Gateway wallet | ### Step 4: Withdraw USDC from Gateway Submit a withdrawal using the [Create a new transaction](/reference/createtransaction) endpoint. Set `subType: VIRTUAL_ACCOUNT` on the **source** to draw from the Gateway wallet instead of a standard asset wallet. `POST /v1/transactions` **Request body (one-time address destination)** ```json theme={"system"} { "assetId": "USDC_ETH_TEST5", "amount": "10", "source": { "type": "VAULT_ACCOUNT", "id": "0", "subType": "VIRTUAL_ACCOUNT" }, "destination": { "type": "ONE_TIME_ADDRESS", "oneTimeAddress": { "address": "0x..." } } } ``` **Request body (vault account destination)** ```json theme={"system"} { "assetId": "USDC_ETH_TEST5", "amount": "10", "source": { "type": "VAULT_ACCOUNT", "id": "0", "subType": "VIRTUAL_ACCOUNT" }, "destination": { "type": "VAULT_ACCOUNT", "id": "1" } } ``` **Field notes:** * `assetId`: the USDC asset ID for the destination chain. * `source.subType`: must be `VIRTUAL_ACCOUNT` for Gateway withdrawals. * `destination`: another Fireblocks vault account or a one-time address. Fireblocks selects which chain to draw from based on your current Gateway balance. The transaction reaches `COMPLETED` when destination-chain delivery is confirmed. ### Step 5: Archive the Gateway wallet (optional) Archiving stops using Gateway on a vault account. It does not move funds. Any USDC already held in Circle Gateway remains and is accessible again by re-activating. See [Deactivate a USDC Gateway wallet](/api-reference/vaults/deactivate-a-usdc-gateway-wallet). ## Automate deposits Instead of submitting each deposit manually (Step 2), you can configure a deposit automation that sweeps USDC from a vault account's asset wallets into its Gateway wallet on a recurring schedule, once the balance clears a threshold you set. ### Set up a deposit automation `POST /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation` See [Set up a USDC Gateway deposit automation for a vault account](/api-reference/vaults/set-up-a-usdc-gateway-deposit-automation-for-a-vault-account). Returns an error if an automation already exists for this vault account and asset — use `PATCH` to change an existing one instead. **Request body** ```json theme={"system"} { "automationType": "USDC_GATEWAY_DEPOSIT", "assetId": "USDC_ETH", "timeBased": { "intervalValue": 60, "intervalUnit": "MINUTES", "balanceThreshold": "1000" } } ``` **Field notes:** * `automationType`: must be `USDC_GATEWAY_DEPOSIT`. * `assetId`: optional. Scopes the automation to a single Fireblocks asset ID; omit to cover all supported USDC Gateway assets. * `timeBased.intervalValue` / `timeBased.intervalUnit`: how often the automation runs. `intervalUnit` is one of `MINUTES`, `HOURS`, or `DAYS`. * `timeBased.balanceThreshold`: minimum USDC balance required before a deposit runs. Set to `"0"` to sweep the full available balance every time, with no minimum. **Response** ```json theme={"system"} { "automationId": "b68a9e08-b59c-4ff9-893f-52d4f78c21e6" } ``` ### Read configured automations `GET /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation` See [Read the USDC Gateway deposit automations for a vault account](/api-reference/vaults/read-the-usdc-gateway-deposit-automations-for-a-vault-account). **Response** ```json theme={"system"} { "settings": [ { "automationId": "b68a9e08-b59c-4ff9-893f-52d4f78c21e6", "vaultAccountId": "42", "assetId": "USDC_ETH", "automationType": "USDC_GATEWAY_DEPOSIT", "timeBased": { "intervalValue": 60, "intervalUnit": "MINUTES", "balanceThreshold": "1000" } } ] } ``` ### Change an automation `PATCH /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation/{automationId}` See [Change a USDC Gateway deposit automation](/api-reference/vaults/change-a-usdc-gateway-deposit-automation). Only the schedule (`timeBased`) can be changed; `automationType` and `assetId` are fixed for the lifetime of the automation. **Request body** ```json theme={"system"} { "timeBased": { "intervalValue": 30, "intervalUnit": "MINUTES", "balanceThreshold": "500" } } ``` ### Stop an automation's schedule `DELETE /vault/accounts/{vaultAccountId}/virtual_asset_wallet/usdc_gateway/deposit_automation/{automationId}` See [Stop a USDC Gateway deposit automation's schedule](/api-reference/vaults/stop-a-usdc-gateway-deposit-automations-schedule). This stops the schedule without deleting the automation's configuration — turn it back on later with `PATCH`, without setting it up again from scratch. ## Supported chains For the current list of chains supported by USDC Gateway, refer to the [Gateway supported blockchains](https://support.fireblocks.io/hc/en-us/articles/27420202963612-USDC-Gateway-Prerequisites-and-Setup-Guide#h_01KTM72K9G6VQ9J2DN3SQHMAQW). ## Limits and fees For current limits and fees, see [Limits and Fees](https://support.fireblocks.io/hc/en-us/articles/27420202963612-USDC-Gateway-Prerequisites-and-Setup-Guide#h_01KREPP3EFEEVTVZRFRGHQWQSW) in the USDC Gateway Setup Guide. ## Related * [Create vault wallets](/reference/create-vault-wallet): create USDC asset wallets before activating Gateway. * [Set transaction authorization policy](/docs/set-transaction-authorization-policy): configure `TRANSFER` and `APPROVE` rules. * [Work with Gas Station](/docs/work-with-gas-station): automate native gas for deposit transactions. * [EVM Gasless (Universal Gasless)](/docs/evm-gasless): let a relay pay the gas for deposit transactions instead. * [Monitoring transaction statuses](/reference/monitoring-transaction-status): track deposit and withdrawal progress via webhooks. # Use Cases Overview Source: https://developers.fireblocks.com/docs/use-cases # What can you build with Fireblocks? Virtually anything! Fireblocks supports a range of use cases, from centralizing treasury management to building a fintech application. Here are some of the use cases we support: * NFT Marketplaces and Platforms * Treasury Management * Digital Asset Custody * CeFi and DeFi Trading * Cross Border Payments * Web3 Gaming * Tokenization * Staking * Smart Contract Security and Management * Wallets for Retail Applications # What types of businesses are built on Fireblocks? Fireblocks enables your business to access the digital asset ecosystem quickly and securely. From Web3 to financial services, almost every type of business is building on Fireblocks. | Industry | Type | | - | - | | Financial Institutions | Banks, Hedge Funds, Asset Managers, Lending Desks, OTC Desks, Prime Brokers, Market Makers, Family Offices | | Web3 Companies | NFT Marketplaces, DAOs, DeFi Protocols, GameFi, Web3 Infrastructure Providers, Protocol Foundations, and B2B Web3 Services | | Retail Services | Exchanges, Corporates, Fintechs, Neobanks, Challenger Banks, Investment Platforms | | B2B Services | Payment Service Providers, Banking-as-a-Service Providers | # Fireblocks Feature Landscape * [Wallet-as-a-Service](/docs/wallet-as-a-service) * [Self-Custody Infrastructure](/docs/self-custody-infrastructure) * [Tokenization](/docs/tokenization) * [Treasury Management](/docs/treasury-management) # Validate Balances Source: https://developers.fireblocks.com/docs/validate-balances How to reconcile incoming deposits against the blockchain with Fireblocks webhooks before you credit end users, and when to fall back to polling. Before you credit end users for incoming transactions, confirm that each deposit is reflected in the vault balance and on the blockchain. Webhooks give you everything you need for this check. Subscribe to these events: * [`transaction.status.updated`](/reference/webhooks-structures-eventtypes-transaction): transaction lifecycle. It includes the `blockInfo` object, with `blockHeight` and `blockHash` for the block that includes the transaction. * [`vault_account.asset.balance_updated`](/reference/webhooks-structures-eventtypes-wallet): balance changes. Each event carries the full balance (`total`, `pending`, `frozen`, `lockedAmount`, `staked`) and, when available, `blockHeight` and `blockHash`. See [VaultAccountBalanceUpdate](/reference/vault-objects#vaultaccountbalanceupdate). ## Reconcile before you credit Reconcile every deposit against the blockchain before you credit end users. This independent check complements the accuracy controls Fireblocks applies to balance data. Follow these steps: 1. Wait for `transaction.status.updated` with status `COMPLETED`. Note `blockInfo.blockHeight`. 2. Wait for a `vault_account.asset.balance_updated` event for the same vault account and asset with a `blockHeight` equal to or greater than the transaction's `blockHeight`. 3. Compare the balance with the blockchain state at that height. If they don't match, don't credit the end user until you investigate and resolve the difference. `blockHeight` and `blockHash` can be missing for some asset types or in specific scenarios. In that case, order balance events by their `createdAt` timestamp and rely on the check against the blockchain in step 3. For some assets, such as TRX and SOL, a balance event may not follow a completed transaction. See [When to poll](#when-to-poll). The diagram shows this flow: ```mermaid theme={"system"} sequenceDiagram participant BC as Blockchain participant FB as Fireblocks participant C as Your backend FB->>C: transaction.status.updated (COMPLETED, blockInfo.blockHeight) FB->>C: vault_account.asset.balance_updated (blockHeight >= transaction blockHeight) C->>BC: Check balance at that block height BC-->>C: On-chain balance alt Balances match C->>C: Credit the end user else Balances differ C->>C: Hold the credit and investigate end ``` ## Handle balance event ordering For general webhook handling, see: * [Best practices for webhooks](/reference/webhooks-best-practices) for duplicate events and delivery order. * [Responses and retries](/reference/webhooks-gettingstarted-responsesretries) for delivery and retry behavior. * [Validating webhooks](/reference/validating-webhooks) for signature verification. Balance events need two extra rules: * Each balance event contains the full balance, not a delta. Replace your stored balance with the latest event and ignore events at a lower `blockHeight`. * Several balance events can share a `blockHeight` (for example, when funds are locked or unlocked). Compare `blockHash` as well, because a reorg can change the hash at the same height. ## When to poll Polling is a fallback, not the default. Use it only in the cases below, and keep requests low to stay within your [rate limit](/reference/rate-limiting) tier. * **Assets that complete without a pending state** (for example, TRX and SOL). The transaction reaches `COMPLETED`, but a balance event may not follow when the balance didn't change. After the `COMPLETED` webhook, call [Get the asset balance for a vault account](/api-reference/vaults/get-the-asset-balance-for-a-vault-account) (`GET /vault/accounts/{vaultAccountId}/{assetId}`) once and apply step 3 of [Reconcile before you credit](#reconcile-before-you-credit). * **Startup and catch-up.** Read the current balance when a consumer starts, after downtime, or when you add a subscription. Webhooks only carry changes. The balance endpoint returns the same fields as the balance webhook. Use it to fetch state, not as an independent verification. Validate against the blockchain. # Estimate Transaction Fees Source: https://developers.fireblocks.com/docs/verify-fee-effeciency Fireblocks provides clients with two API endpoints to estimate transaction fees: 1. **Estimate Transaction Fee** - [Documentation](/reference/estimatetransactionfee) 2. **Estimate Network Fee** - [Documentation](/reference/estimatenetworkfee) # Estimate Transaction Fee The [`Estimate Transaction Fee` endpoint](/reference/estimatetransactionfee) is designed to simulate a real transaction and calculate its potential fee. This process considers factors such as the source and destination amounts, the number of inputs and outputs, and other relevant transaction details. Essentially, it mimics the actual transaction to determine what the fee would be if executed. When using this endpoint, clients should be aware that it follows the same rules as creating a transaction. This includes requiring the relevant vault wallet in the vault account, ensuring the estimated transaction's balance is in the wallet, and providing a valid destination. While many clients implement logic to estimate the transaction fee before each execution, it is crucial not to call this endpoint for every minor transaction change. For example, in EVM-based transactions, the gas limit for transferring the base asset or a specific token usually remains the same (barring sudden surges in network fees). The main variable is the current network fee, which can be obtained using the second endpoint. # Estimate Network Fee The [`Estimate Network Fee` endpoint](/reference/estimatenetworkfee) returns the current network fee for a specified network. This allows clients to calculate transaction fees or display the current network fee to their users. For instance, it provides the current gas price for ETH, which is part of the entire transaction fee formula, alongside the gas limit and other parameters. The network fee response is cached for 30 seconds on the Fireblocks side. Clients should consider this interval when using this endpoint, as querying it more frequently than every 30 seconds does not provide additional value and the fee level value would remain the same. # Fee Levels Both endpoints return estimated fees or network fees in three levels: LOW, MEDIUM, and HIGH. Here’s how these are determined for different transaction types: ## EVM-Based Transactions To simplify the gas calculation for each transaction, Fireblocks offers preset fees for both the Console (slow, medium, and fast) and the API (low, medium, and high). These fee calculations are based on recent blocks and a certain percentile of the data collected. Fireblocks retrieves the latest block data every minute, inspecting the gas prices of recent blocks to determine the fee rate and automatically set the gas limit. In addition to verifying the latest data, Fireblocks also simulates the desired transactions to estimate the fee. The transaction parameters are sent to an API endpoint on the node, which returns an estimated transaction fee. ## UTXO-Based Transactions * **High**: Fees are based on the previous block’s fee. * **Medium**: Fees are based on the average of the previous two blocks. * **Low**: Fees are based on the average of the last four blocks. Fireblocks' API endpoints for estimating transaction fees provide clients with robust tools to ensure they can accurately predict and manage transaction costs. By using these endpoints, clients can maintain efficient and cost-effective operations while offering transparency and predictability in transaction fees to their users. Whether you are dealing with EVM-based, UTXO-based transactions or any other type of blockchain, understanding and utilizing these endpoints can significantly enhance your transaction management strategy. # Best Practices ## Calling the Fee Estimation Endpoint In some workflows, customers call the fee estimation endpoint for every transaction. However, this is not required for chains with relatively constant per-transaction fees, where fees depend mainly on network congestion and number of operations. In such cases, you can explicitly state the network fee instead (for example, on Stellar). In these flows, the network fee can be cached for several seconds, accounting for differences between chains. > [!WARNING] > > **Fee Estimation Accuracy** > > Fee estimation endpoints return the estimated fee at the time of the call. The actual fee may change when the transaction is created due to factors such as network congestion or different UTXO selection caused by newer deposits. # Learn more about Fees 1. [EVM Networks](https://support.fireblocks.io/hc/en-us/articles/10615179965596-EVM-fees-Overview) 2. [UTXO Based Assets](https://support.fireblocks.io/hc/en-us/articles/10616193193756-UTXO-fees-Overview) 3. [Unique Calculations](https://support.fireblocks.io/hc/en-us/articles/13995244288412-Solana-priority-fee) For more details, see the [Fee Management Developer Guide](/reference/estimate-transaction-fee). # Wallet-as-a-Service Source: https://developers.fireblocks.com/docs/wallet-as-a-service Easily build and integrate custom, secure wallets into your application with Fireblocks' Wallet-as-a-Service API # What is Wallet-as-a-Service? Fireblocks Wallet-as-a-Service (WaaS) is an API-based solution that includes the most secure MPC wallets by design. Our APIs generate ECDSA and EdDSA signatures to sign any transaction. It enables anyone to create, manage, and secure wallets for any number of users without performing the security work themselves. [Start developing on Fireblocks today](https://www.fireblocks.com/developer-sandbox-sign-up/?gl=1*19mlzew*_ga*MTY4ODc5Mjc3Ni\[%E2%80%A6]_ga_D39L1D2ZX2*MTY4Nzg2MTQwOS4xNi4xLjE2ODc4Njc3OTMuMjcuMC4w). # Manage your digital assets with Fireblocks WaaS You can easily plug and play Fireblocks Wallet-as-a-Service into your app — allowing your users to own and store digital assets — or facilitate your users to buy, sell, and trade cryptocurrencies, all with our self-custody solutions: * **Scale without sacrificing security:** With our secure MPC wallets, you can create, manage, and secure deposits for any number of end-users in a compliant and reliable manner. * **Supports businesses of all sizes:** Our WaaS product is compatible with various industries, including retail service, web3 companies, financial services and banking, exchanges, and financial market infrastructures. * **Native support for 150+ blockchains:** Store your assets on the blockchain of your choice. And even if we don't support your blockchain, we offer raw signing so you can securely manage assets. * **Monitor statuses of your transactions:** Using our tools like transaction status API, webhooks, and transaction history, you can stay up-to-date on the progress of your transactions and take action as needed. * **Policy Engine:** Protect your custody from internal collusion, human error, and external attacks. With the Fireblocks API, you can manage your workspace, automate your transaction flow, or use webhooks to receive push notifications on what's happening in your workspace. And in addition to the APIs, our Console can help monitor transactions and audits securely and reliably. Learn more about [Fireblocks Wallet-as-a-Service](https://www.fireblocks.com/platforms/mpc-wallet/). # Guides ## Deploying an NFT collection Create an NFT collection using the ERC-721 standard and [deploy it with Fireblocks Hardhat Plugin](/reference/deploy-an-nft-collection). ## Creating Vaults and Wallets [Generate Fireblocks vault accounts and asset wallets](/docs/create-direct-custody-wallets) at scale for both account-based and UTXO asset wallet types using the Fireblocks API and SDKs. ## Minting an NFT [Mint and verify Non-Fungible Tokens (NFT)](/reference/mint-an-nft) for your NFT collection using the Fireblocks Hardhat Plugin or Web3 Provider. ## Raw Message Signing Support more chains and actions. Generate ECDSA and EdDSA signatures to [sign any transaction type or message](/docs/raw-signing). ## Typed Message Signing Allows you to [sign messages using specific standard formats](/docs/typed-message-signing-1) that prefix the message with a magic string. For ETH\_MESSAGE and EIP712 messages. ## Creating a Transaction Use transaction API calls to [initiate any Fireblocks transaction operation type](/transactions/create-transactions), such as transfer, mint, burn, contract call, RAW, and more. # Developer Community Want to learn more from Fireblocks knowledge experts and other developers? [Join our developer community today](https://community.fireblocks.com/)! # Wallet Pools Source: https://developers.fireblocks.com/docs/wallet-pools Group vault accounts into a Wallet Pool and send from it as one logical source, with health-aware member selection, Policy setup, and reconciliation. ## Overview New to Wallet Pools? See the [Wallet Pools](https://support.fireblocks.io/hc/en-us/articles/27776980063260-Wallet-Pools) Help Center article for a product introduction and the Console walkthroughs. A Wallet Pool groups vault accounts under a single logical source. Name the pool as a transaction source and Fireblocks selects one member vault account to send from, preferring members that are not congested. On EVM chains each vault account has one sequential nonce stream, so a transaction that fails to confirm blocks every later transaction from that account until it clears. A single hot vault account is therefore both a throughput ceiling and a single point of failure. A pool removes the need to build vault rotation and stuck-transaction tracking in your own infrastructure. A pool is a protected tag with `type: "WALLET_POOL"`, so it inherits protected tag governance: membership changes are quorum-approved and Policy rules can reference the pool directly. There are no wallet-pool endpoints. Pools are created, populated, and used through the existing tags, vaults, and transactions endpoints. See [Tags](/docs/tags) for the tag lifecycle these calls share. > [!NOTE] > > A pool distributes transactions, not funds. Balances are never summed across members. See [Limitations](#limitations). ## Use cases A pool solves the same congestion problem in three places. In each case you create the pool the same way, then point the relevant flow at it instead of at a single vault account. * **Withdrawal vaults.** High-volume withdrawals run through a small set of hot vault accounts, where one stuck transaction blocks every later withdrawal from that account until it clears. A pool spreads withdrawals across several vault accounts, skips the congested ones, and rotates across multiple on-chain addresses so your withdrawal pattern is less predictable. Set the pool as the transaction source, as in [Step 5](#step-5-send-from-the-pool). * **Gas Station tank.** Gas Station fuels every vault account in your workspace from a single gas tank, so on EVM all fueling shares one sequential nonce stream. That stream stalls exactly when you can least afford it, during a traffic spike when many vault accounts need topping up at once. A pool spreads the fueling across several vault accounts. In **Settings** > **Gas Station tank**, select a Wallet Pool instead of a single vault account. * **Gasless relay.** With gasless transactions your end users hold no native asset, and a relayer vault account signs and pays gas on their behalf. That relayer is one vault account with one nonce stream, so it becomes the bottleneck as gasless volume grows. A pool spreads relaying across several vault accounts. In **Settings** > **Initiate gasless transactions** > **EVM**, select a Wallet Pool as the relayer source. See [EVM Gasless (Universal Gasless)](/docs/evm-gasless#use-a-wallet-pool-as-the-relay). Relayer selection uses the same tiering and rotation as a transfer, but sizes its requirement against a gas budget rather than a transfer amount, and uses a higher limit when the end user's account needs delegating first. A relayer pool therefore needs more native-asset headroom per member than a transfer pool of comparable volume. Use a separate pool per role, since the balance profiles differ. ## Requirements * **Roles.** Creating a pool requires the Owner, Admin, Non-Signing Admin, or Editor role and needs no approval. Adding or removing members requires the same roles to submit, plus an Owner, Admin, or Non-Signing Admin to approve. There is no API to approve; approvers act in the Fireblocks mobile app or under Pending Approvals in the Console. * **SDK.** `WALLET_POOL` as a transfer peer type requires `@fireblocks/ts-sdk` 24 or later. Raw HTTP works on any version. * **Members.** Vault accounts only. Each member needs the transfer asset and the chain's native asset. ## API flow ```mermaid theme={"system"} flowchart LR A[Create pool tag] --> B[Attach vault accounts] B --> C[Approval clears] C --> D[Add Policy rule] D --> E[Send with pool source] ``` ### Step 1. Create the pool `POST /v1/tags` **Request body** ```json theme={"system"} { "label": "Withdrawal Vaults", "description": "Hot withdrawal pool", "type": "WALLET_POOL", "isProtected": true } ``` **Response** ```json theme={"system"} { "id": "588af612-c2e6-491a-8bb4-86c85868f42f", "label": "Withdrawal Vaults", "isProtected": true, "type": "WALLET_POOL", "updatedAt": 1786549844000 } ``` The tag's `id` is the pool ID, and every later step refers to it. Pool names are unique across the workspace and collide with protected tag names as well as other pools. For the full field reference on this endpoint, including label rules and per-request limits, see [Create a tag](/docs/tags#step-1-create-a-tag). > [!NOTE] > > `isProtected: true` does not identify a pool. Ordinary protected tags are also `isProtected: true`, and they omit `type` entirely. To tell a pool from an ordinary protected tag, check that `type` is `WALLET_POOL`. ### Step 2. Attach vault accounts `POST /v1/vault/accounts/attached_tags` **Request body** ```json theme={"system"} { "vaultAccountIds": ["12", "13", "14"], "tagIdsToAttach": ["588af612-c2e6-491a-8bb4-86c85868f42f"] } ``` **Response** ```json theme={"system"} { "appliedOperations": [], "pendingOperations": [ { "vaultAccountId": "12", "tagId": "588af612-c2e6-491a-8bb4-86c85868f42f", "action": "ATTACH", "approvalRequestId": "9f2c1e77-4b0a-4d21-8e6b-1c5a3f0d9b84" } ], "rejectedOperations": [] } ``` | Field | Notes | | - | - | | `appliedOperations` | Always empty for pools, since attachments require approval. | | `pendingOperations` | Queued for approval. Each entry carries an `approvalRequestId`. | | `rejectedOperations` | Never queued. Each entry carries a `reason`: `PENDING_REQUEST_EXISTS` (a change for this pair is already awaiting approval), `ATTACHMENT_ALREADY_EXISTS`, `ATTACHMENT_DOES_NOT_EXIST`, or `CAPACITY_EXCEEDED`. | Only one membership change per vault account and pool can be in flight, so serialize attachments. For idempotent provisioning, treat `ATTACHMENT_ALREADY_EXISTS` on attach and `ATTACHMENT_DOES_NOT_EXIST` on detach as success. Detaching uses the same call with `tagIdsToDetach`, and follows the same approval flow. Removing a member resets the rotation pointer to the first member. ### Step 3. Track the approval `GET /v1/tags/approval_requests/{approvalRequestId}` **Response** ```json theme={"system"} { "id": "9f2c1e77-4b0a-4d21-8e6b-1c5a3f0d9b84", "type": "TAG_ATTACH_DETACH", "state": "PENDING" } ``` `state` is one of `PENDING`, `APPROVED`, `REJECTED`, `FAILED`, `CANCELLED`, or `EXPIRED`. Poll until it is terminal and treat anything other than `APPROVED` as the change not having happened; `EXPIRED` and `CANCELLED` are silent no-ops that leave the pool empty. A pool uses the same approval machinery as any protected tag, so see [Protected tags and the approval flow](/docs/tags#protected-tags-and-the-approval-flow) for what each state means. Confirm membership before sending, since an empty pool rejects every transaction. Read the pool's members with `GET /v1/vault/accounts_paged?includeTagIds={poolId}`, and list your pools with `GET /v1/tags?type=WALLET_POOL`. ### Step 4. Allow the pool in your Policy > [!WARNING] > > Fireblocks resolves the pool to one member and then evaluates Policy against **that member**, not against the pool. A rule that permits only specific vault account IDs as sources blocks any transaction that resolves to a different member, so the same request succeeds and then fails as routing moves between members. This is the most common cause of a failed first pool transaction. In the Console, go to Policies, add or edit a rule, and under source select the pool from the **Wallet Pools** category. Set destinations to cover everywhere the pool needs to reach. Referencing the pool also keeps the rule stable as your vault inventory grows: add or remove members through the approval flow and both routing and enforcement follow, with no Policy edit. ### Step 5. Send from the pool `POST /v1/transactions` **Request body** ```json theme={"system"} { "assetId": "ETH", "amount": "0.5", "source": { "type": "WALLET_POOL", "id": "588af612-c2e6-491a-8bb4-86c85868f42f" }, "destination": { "type": "ONE_TIME_ADDRESS", "oneTimeAddress": { "address": "0xRecipientAddress" } } } ``` `source.id` is the pool ID: the `id` returned when you created the pool tag in Step 1, not a vault account ID. Everything other than `source` behaves as it does for a vault-account source, including the standard minimal response. **Response** ```json theme={"system"} { "id": "f1375359-967d-4b09-a588-f0be8228356c", "status": "SUBMITTED" } ``` ## Identify the member that sent Sending from a pool does not depend on this. Read the member back when you want it for reconciliation or your own reporting, and whenever you might later boost or cancel the transaction, which has to target the member directly. Use `GET /v1/transactions/{txId}` or the transaction webhook. Both carry the same fields. **Response** ```jsonc theme={"system"} { "id": "f1375359-967d-4b09-a588-f0be8228356c", "assetId": "USDT_ERC20", "source": { "id": "10174", // the member Fireblocks selected "type": "VAULT_ACCOUNT", // rewritten from WALLET_POOL after resolution "name": "Withdrawal 1", "tags": [ { "id": "588af612-c2e6-491a-8bb4-86c85868f42f", "label": "Withdrawal Vaults" } ] }, "status": "COMPLETED", "note": "Wallet pool tx: 0.01 USDT_ERC20 from Withdrawal Vaults pool to Deposit 3", "extraParameters": { "walletPoolId": "588af612-c2e6-491a-8bb4-86c85868f42f" } } ``` | Field | Notes | | - | - | | `source.id` | The member vault account Fireblocks selected for this transaction. | | `extraParameters.walletPoolId` | The pool ID. Reconcile on this. It survives a rename and survives deletion of the pool, so historical transactions stay attributable. | | `note` | Auto-populated. Readable, but the format is not a contract; do not parse it. | > [!WARNING] > > `GET /v1/transactions?sourceType=WALLET_POOL` does not filter. The parameter is accepted, but the result set is identical to an unfiltered query, so it returns non-pool transactions as well. Passing the pool ID as `sourceId` returns nothing. Select client-side on `extraParameters.walletPoolId`, which is the only reliable marker. > [!WARNING] > > **Boost and cancel target the member, not the pool.** To replace a stuck pool transaction by fee, or to drop it, submit the replacement with `source.type: VAULT_ACCOUNT` and the `source.id` you read back above. A replacement submitted with `source.type: WALLET_POOL` runs member selection again and may resolve to a different member, whose address and nonce are not the ones the stuck transaction used, so it cannot replace it. Keep the resolved member on your own transaction record if you may need to boost or cancel later. See [Boost Transactions](/docs/boost-transactions). ## How Fireblocks selects a member Five stages. Only the first two can reject the transaction. 1. **Asset filter.** Members with no wallet for the requested asset are dropped. If none remain, the transaction is rejected. 2. **Balance filter.** Each remaining member is checked individually for `available >= amount`. If none passes, the transaction is rejected for insufficient balance. This runs before any health lookup. 3. **Fee preference.** Members that can also cover the native-asset fee are preferred. If none can, all funded members stay in play and the transaction still proceeds. Skipped where Fireblocks cannot estimate a fee for the chain. 4. **Health tiers.** Account Traffic Control reports each candidate as `HEALTHY`, `DEGRADED`, or `BLOCKED`. Fireblocks takes the best non-empty tier. A pool whose every member is `BLOCKED` still sends, so health affects ordering, not eligibility. 5. **Round-robin** within that tier. The counter is keyed on tenant, pool, and **base asset**, so assets sharing a base asset share one counter. It advances only after the transaction is created, so a failed create does not burn a slot. > [!WARNING] > > When Account Traffic Control returns nothing, including on every non-EVM chain, stages 4 and 5 are replaced by balance scoring: sort by locked ascending then available descending, and take the first. That is deterministic, not rotating. It returns the same member on every transaction until balances shift, so on non-EVM chains a pool delivers neither congestion avoidance nor address rotation. ## Limitations * **No aggregated balance.** One member must cover the full amount alone. A pool of ten accounts holding 1 ETH each cannot send 5 ETH; it is rejected for insufficient balance. Size members against your largest single transaction, not your aggregate volume. * **Gas funding is not enforced.** A member that cannot cover the fee is deprioritized, not excluded, so it can still be selected and produce a transaction that stalls. Alert on the minimum native balance across members. * **Health-aware routing is EVM only.** Non-EVM chains use the deterministic fallback above. * **Rotation is not a strict sequence.** State is per-node and in memory, and removing a member resets the pointer. Never build reconciliation or nonce prediction on a fixed member order. * **Shared members share health.** A vault account can belong to several pools, and volume driven through one affects how another sees it. * **No rebalancing.** Fireblocks does not move funds between members. Drive your own rebalancing from the smallest member's balance, not the pool total. * **Deleting a pool requires it to be empty.** Detach every member and clear that approval first. Renaming and deleting both require approval. ## Troubleshooting | Symptom | Cause and fix | | - | - | | Rejected for insufficient balance while the pool holds plenty | Balances are not summed. Top up a single member above your largest transaction. | | Rejected: asset not found | No member holds a wallet for the asset. Activate it on the members and fund it. | | Rejected: no vault accounts for the pool | The attachment approval has not completed, or it expired. Check the request state. | | Blocked by Policy, intermittently | A rule scopes sources to vault account IDs. Rewrite it against the pool. See Step 4. | | Sent from a member that could not pay gas | Fee coverage is a preference. Keep every member funded with native asset. | | Error `1904` on fee estimation | Pool sources are unsupported. Estimate against a member vault account. | | The same member every time | Expected on non-EVM chains and whenever health data is unavailable. On EVM, check whether the other members are failing the balance or asset filter. | | A boost or drop did not replace the stuck transaction | The replacement used the pool as its source, so member selection ran again. Resubmit it with `source.type: VAULT_ACCOUNT` and the member ID from the original transaction. | | Pool name rejected as in use | Names collide with protected tags too. [List all tags](/docs/tags#step-4-list-update-and-delete-tags) when checking availability, not just pools. | ## Related * [Wallet Pools](https://support.fireblocks.io/hc/en-us/articles/27776980063260-Wallet-Pools) for the Console walkthroughs, supported chains, and approval behavior * [Tags](/docs/tags) for the tag lifecycle, attachment buckets, and approval states a pool inherits * [Set Policies](/docs/set-transaction-authorization-policy) for writing the rule that permits the pool * [Work with Gas Station](/docs/work-with-gas-station) for tank thresholds and auto-fueling * [Boost Transactions](/docs/boost-transactions) for replacing or dropping a stuck EVM transaction * [Create transactions](/transactions/create-transactions) for the full transaction request reference # Wallet Link Source: https://developers.fireblocks.com/docs/web3-wallet-link # About the Fireblocks Wallet Link As the demand for Web3 accessibility and usage grows, more users want to use dApps directly to manage their NFTs, get rewards, and more. The Fireblocks Wallet Link allows your users to connect their Fireblocks wallet addresses directly to Web3 applications (dApps), without your users needing to use their own wallet or any other application. This enables you to offer your users one-stop shopping for anything related to crypto and Web3. # Creating a Wallet Link connection The Wallet Link currently supports [WalletConnect](https://walletconnect.com/) connections. WalletConnect is a well-established standard for connecting wallets and Web3 applications and is supported by all of the leading dApps today. When a user wants to connect to a dApp, they scan a QR code provided by WalletConnect inside the dApp. This QR code contains a URI that represents the connection session to be established between the dApp and the wallet. Passing this URI onto Fireblocks using the API allows the connection to be successful. To initiate a new connection, pass the URI as follows. ```typescript TypeScript theme={"system"} const examplePayload = { feeLevel: "MEDIUM", vaultAccountId: 0, uri: "wc:f61647f4-7f98-4cb7-95d2-1db8e58fb0bb@1?bridge=https%3A%2F%2Fv.bridge.walletconnect.org&key=d145d64bda22f2be8fb23c251116b0cd8e3613f6971d193ff89b24e6735aaa6c" // The WalletConnect QR code } const connectionResponse: CreateWeb3ConnectionResponse = await fireblocks.createWeb3Connection("WalletConnect", payload); ``` > [!NOTE] > > **For Web3 connections from a Non Custodial Wallet, please use the payload below:** > > ```javascript theme={"system"} > const examplePayload = { > feeLevel: "MEDIUM", > ncwId: "b8337f1d-bd61-4d6c-afc1-4c9d60aa2132", > ncwAccountId: 0, // or any other account in the NCW > uri: "wc:f61647f4-7f98-4cb7-95d2-1db8e58fb0bb@1?bridge=https%3A%2F%2Fv.bridge.walletconnect.org&key=d145d64bda22f2be8fb23c251116b0cd8e3613f6971d193ff89b24e6735aaa6c" // The WalletConnect QR code > } > ``` The response from Fireblocks includes the relevant data about the connection, such as connection ID, dApp URL, and so on. ```typescript TypeScript theme={"system"} connectionResponse === { id: "f3ca1e41-378e-4718-b8d3-51dddb3777d3", sessionMetadata: { // Provided by the dApp "appUrl": "https://www.someDapp.com", "appName": "SomeDapp", "appDescription": "SomeDapp is the best example dapp", "appIcon": "https://static.fireblocks.io/prod/wcs/dappIcon/abc123" } } ``` # Approving a Web3 connection request You can approve or reject the Web3 connection request based on the data in the connection response. ```typescript TypeScript theme={"system"} const approve = true; const result = await fireblocks.submitWeb3Connection("WalletConnect", connectionResponse.id, approve); ``` > [!NOTE] > > If you want to limit your users to only some dApps, simply reject connection requests from any unapproved dApp. # Listing connections To list all your existing Web3 connections, run the following command: ```typescript TypeScript theme={"system"} const examplePayload = { pageCursor: "SomePageCursor" // undefined for first page, pageSize: 10, filter: { vaultAccountId: 0, connectionMethod: "API" }, sort: "createdAt", order: "DESC" } const response = await fireblocks.getWeb3Connections(examplePayload); response.paging === { next: "NextPageCursor" } const exampleSession: Session = response.data[0]; exampleSession === { id: "f3ca1e41-378e-4718-b8d3-51dddb3777d3", userId: "abc-123-456-def", vaultAccountId: 0, chainIds: ["ETH"], feeLevel: "MEDIUM", creationDate: "2023-03-08T11:20:13.823Z", connectionType: "WalletConnect", connectionMethod:"API", sessionMetadata: { "appUrl": "https://www.someDapp.com", "appName": "SomeDapp", "appDescription": "SomeDapp is the best example dapp", "appIcon": "https://static.fireblocks.io/prod/wcs/dappIcon/abc123" } } ``` # Removing a connection To remove a connection, run the following command: ```typescript TypeScript theme={"system"} const result = await fireblocks.removeWeb3Connection("WalletConnect", exampleSession.id); ``` # Signing a dApp-originated transaction Once connected to a dApp, the user can perform an action that will result in a signing operation by Fireblocks. This operation is created by the API user that created the Web3 connection and is subject to the [Policies](https://support.fireblocks.io/hc/en-us/articles/29213873102108-Policies-for-EVM-DeFi-operations) like any other operation. In most cases, a transaction created by a dApp translates into a Contract Call transaction in Fireblocks. Some other operations that require signing on an [off-chain message](https://support.fireblocks.io/hc/en-us/articles/4413379762450-Off-chain-message-signing) will result in a Typed message in Fireblocks, such as signing a registration message when logging into OpenSea. Make sure to adjust your Policy rules accordingly. You can use the Policy as another layer of limitation by whitelisting the relevant contract addresses of any dApp you want to allow while preventing contract calls to any other contract to go through. When working with an [API Co-Signer machine](https://support.fireblocks.io/hc/en-us/articles/12006018592156-API-Co-Signer-Overview), you can use the Callback Handler to let the user approve the transaction on their device before it is signed and broadcasted. # What Is Fireblocks? Source: https://developers.fireblocks.com/docs/what-is-fireblocks Fireblocks is a user-friendly platform that uses direct custody to build new blockchain-based products and manage your digital asset operations. Direct custody is a type of self-custody that seamlessly blends high performance, zero counterparty risk, and multiple layers of security. With Fireblocks, you're always the owner and controller of your assets. The Fireblocks direct custody model follows five core Custody & Risk Principles: 1. Provide an environment with zero counterparty risk. 2. Eliminate external and internal attack vectors. 3. Guarantee business continuity. 4. Ensure granular control and visibility of every transaction. 5. Deliver high-performance products and services with ease of use. [Learn more about Fireblocks’ Custody & Risk Principles.](https://www.fireblocks.com/principles/) The Fireblocks platform is comprised of three core components: ### Digital Asset Wallets Secure, and scalable MPC-based wallets with robust key management to custody digital assets. The Fireblocks MPC-CMP protocol redefines private key security, never gathering a private key as one whole, eliminating risk. Fireblocks customers use our wallets for a range of operations, such as treasury, trading, cold storage, royalties, NFTs, smart contracts, and user wallets. ### Platform Governance The Policy Engine automates governance policies for transaction rules and admin approvals. It enables you to configure a list of rules that dictate how transactions are handled and approved. A rule can set whether a transaction is blocked, approved, or requires additional signers using filters. Policy Engine rules for various destinations, such as internal wallets, network connections, exchanges, fiat providers, whitelisted addresses, and contract wallets. ### Treasury Management Fireblocks' single platform centralizes wallet and address management to simplify crypto and NFT treasury operations. Wallets are organized into Vault accounts (segregated or omnibus) where you can set specific transaction policies to protect the movement of funds. The Fireblocks Network ensures transfers from Fireblocks wallets are simple and secure. The Network automatically authenticates deposit addresses to avoid manual deposit address entry and the need for test transfers. Over 30 exchanges and fiat providers are connected to the Fireblocks Network, enabling you to securely deposit and withdraw from their exchange accounts. Thousands of businesses are also connected to the Fireblocks Network for secure peer-to-peer transfers. # Fireblocks Multi-layer Security Fireblocks has created a multi-layer security matrix that layers MPC, secure enclaves, our signature Policy Engine, and an asset transfer network to provide the strongest software and hardware defense available against evolving attack vectors. Because we understand that no security technology alone is unbreakable, our approach to security protects all attack surfaces in a redundant structure to provide multiple fail-safes, in the event one security control fails. Our security structure provides a truly secure environment for storing, transferring, and issuing digital assets. This ensures that your assets are protected from cyberattacks, internal colluders, and human errors. As a result, Fireblocks serves as the foundation for 1,000s of digital asset businesses and has securely transferred over \$3T in digital assets. ## Multi-Layer Security In-Depth ### Layer 1: MPC-CMP MPC (multi-party computation) is a cryptographic technology that stores secret information with each party, then solves a problem that requires the unshared, decentralized input of all these parties' secret information. Fireblocks uses MPC over other technologies such as Multisig because MPC is protocol agnostic, operationally flexible, and less costly as signing occurs off-chain. Fireblocks developed the MPC-CMP protocol that applies this concept to blockchain-based ECDSA and EdDSA signatures (used by all blockchains). The Fireblocks MPC-CMP protocol redefines private key security, never gathering a private key as one whole. MPC-CMP also requires fewer transaction rounds for signing (8x faster than standard MPC) and is available with cold storage signing where key shares are stored offline. Fireblocks distributes the cryptographic MPC shares across multiple tier-1 cloud environments to ensure an extra layer of security even if one of the physical data centers is compromised. You can also store MPC shares across on-prem data centers or configure a hybrid scenario. ### Layer 2: Secure Enclaves Fireblocks utilizes Intel SGX, a hardware-level enclave that isolates selected code and data within a system. It is designed to protect the cryptographic material, the cryptographic algorithm (MPC and ZKPs), and the execution of sensitive parts of the software from both insiders (such as rogue admins) and hackers. As the MPC key shares are stored in SGX, they cannot be extracted even if malware or a hacker has control over the server’s OS – as the memory space and the data in the SGX enclave are encrypted. We also utilize SGX to secure API keys. In the trusted execution environments (TEEs) where we store these exchange credentials, the information cannot be retrieved by hackers, inside colluders, or even Fireblocks employees. ### Layer 3: Policy Engine Fireblocks’ Policy Engine allows you to configure a list of rules that affect how transactions are handled and approved. A rule can set whether a transaction is blocked, approved, or requires additional signers using filters such as source, destination, asset, and amount. Fireblocks secures the Policy Engine itself using SGX and distributes policy verification across several MPC servers. Policy rules are signed by a quorum of admins and encrypted within SGX; the engine is implemented inside of the SGX enclave and the code cannot be modified. This prevents both hackers and even insiders from modifying the implemented rules or the logic of the policy engine. ### Layer 4: Fireblocks Network The Fireblocks Network is an institutional asset transfer network that completely mitigates the risks associated with deposit addresses by automating deposit address authentication and rotation. The Fireblocks Network entirely removes the need to copy and paste deposit addresses, then authenticate them using test transfers and whitelisting procedures. Without an authentication network, it’s possible for assets to be lost through deposit address spoofing or human errors (such as entering a deposit address for a counterparty that they’ve already rotated out). ## Additional Security Measures ### Admin Quorum The Admin Quorum defines the minimum number of administrators required to approve connections and workspace changes. This includes whitelisting addresses, approving network connections, exchange accounts, external destination addresses, approving new users, and approving other workspace configuration changes. The admin quorum prevents insider attacks, such as an administrator trying to whitelist their personal wallet address to steal funds. ### Multi-factor Authentication Two-factor authentication is required at a minimum for all Fireblocks users. Any authenticator app may be used such as apps from Google, Microsoft, LastPass, or Yubico. ### Logging All activity within the Fireblocks workspace, including administrative changes, and transactions are securely logged for auditing purposes. These logs can be viewed natively within the Fireblocks console, or exported to any log aggregation system, such as a SIEM. # What You Can Connect Source: https://developers.fireblocks.com/docs/what-you-can-connect See which screening checks and providers you can enable, how each is set up, and which workspace types support it. Some checks you set up in your own code through the API; others an admin connects in the Console. When several are enabled, they run in this order: Address Registry Screening, AML/KYT, Bring Your Own Screening, Travel Rule. ## Availability and requirements | Provider | Pricing | Enabled by | You provide | Workspaces | | - | - | - | - | - | | [Address Registry Screening](/docs/address-registry-screening) | Free | You activate it | Nothing | Testnet, Mainnet | | [Chainalysis](/docs/chainalysis) | Premium | Fireblocks Support | API key | Testnet, Mainnet | | [Elliptic](/docs/elliptic) | Premium | Fireblocks Support | API key and secret | Mainnet | | [TRM Labs](/docs/trm-labs) | Premium | Fireblocks Support | API key | Mainnet | | [Bring Your Own Screening](/reference/bring-your-own-screening-check-developer-guide) | Premium, Early Access | Your Customer Success Manager | Nothing | Developer Sandbox, Testnet, Mainnet | | [Notabene](/docs/notabene) | Premium | Fireblocks Support | API key, secret, and VASP DID | Developer Sandbox, Testnet, Mainnet | | [Sumsub](/docs/sumsub) | Premium | Fireblocks Support | Nothing; Sumsub sends your credentials to Fireblocks | Developer Sandbox, Testnet, Mainnet | | [GTR](/docs/gtr) | Premium | Fireblocks Support | API key and secret | Developer Sandbox, Testnet, Mainnet | | [TRUST](/docs/interact-with-trust) | Free | No enablement needed | Nothing | Developer Sandbox, Testnet, Mainnet | ## What you can set up through the API | Check | Provider | Task | Through | Guide | | - | - | - | - | - | | Address Registry Screening | Fireblocks | Manage counterparty groups, activate | API | [About Address Registry Screening](/docs/address-registry-screening) | | Address Registry Screening | Fireblocks | Build the policy | Console only | [About Address Registry Screening](/docs/address-registry-screening) | | AML/KYT | Chainalysis, Elliptic, TRM Labs | Connect the provider | Console only | [AML/KYT Overview](/docs/aml-kyt-overview) | | Bring Your Own Screening | Your own process or provider | Activate and submit verdicts | API | [About Bring Your Own Screening](/reference/bring-your-own-screening-check-developer-guide) | | Travel Rule | Notabene, Sumsub, GTR, TRUST | Integrate the Fireblocks SDK to create and attach Travel Rule messages | API | [Travel Rule Overview](/docs/travel-rule-overview) | | Travel Rule | Notabene | Connect the provider | Console only | [Notabene](/docs/notabene) | | Travel Rule | Sumsub, GTR | Connect and disconnect the provider | API | [Sumsub](/docs/sumsub), [GTR](/docs/gtr) | | Travel Rule | TRUST | Publish proofs of address ownership | API | [TRUST](/docs/interact-with-trust) | ## Limits * **One AML/KYT provider per workspace.** Different workspaces can use different providers. * **Combining Travel Rule providers:** Sumsub and GTR can be active on the same workspace; Notabene cannot run alongside Sumsub or GTR. TRUST works alongside any provider. * **Address Registry Screening** is not available on European or Swiss environments. * **Switching or disconnecting** a provider goes through Fireblocks Support, except Sumsub and GTR, which you can disconnect through the API with [Disconnect customer integration](/api-reference/trlink/disconnect-customer-integration). Disconnecting removes the provider's policy and permanently deletes its stored credentials. ## On the Help Center To connect providers in the Fireblocks Console and see how the checks fit together, see [What You Can Connect](https://support.fireblocks.io/hc/en-us/articles/30614328257692-What-You-Can-Connect). # Overview Source: https://developers.fireblocks.com/docs/whitelist-addresses To improve the security of transactions from your workspace, we recommend whitelisting destination addresses. Whitelisting addresses is a general security best practice that helps prevent potential loss of funds. It protects against issues such as malicious address manipulation or human error, like copying and pasting the wrong address and inadvertently sending funds to an unintended recipient. Each whitelisting request needs approval from the Admin Quorum before funds can be transferred to that address. If any admin rejects the request, the address will not be whitelisted. However, you can submit a new request to whitelist the same address, which can be approved by the Admin Quorum at any time. In Fireblocks, there are three types of whitelisted address entities, each capable of holding different asset addresses: * **External Wallets** * **Internal Wallets** * **Contracts** > [!TIP] > > **Best Practice:** If you interact with a specific address multiple times, it should be added to the whitelisted addresses in your workspace. > [!WARNING] > > **Default Behavior:** By default, to send funds to an address outside of Fireblocks, the address must first be whitelisted. If you want to permit transactions to non-whitelisted addresses, you should enable the [One Time Address (OTA) feature](/docs/whitelist-addresses#work-with-one-time-addresses) in your workspace. # Whitelisted Addresses Types ## External Wallets External Wallets are entities that hold addresses external to your Fireblocks workspace and are not under your ownership. These addresses belong to your clients or counterparties. If you intend to whitelist an address that is not under your control, it should be added to the External Wallet entity. Note that External Wallet addresses do not display the on-chain balance of the whitelisted address. ## Internal Wallets Internal Wallets are entities that hold addresses external to your Fireblocks workspace but are under your ownership. These addresses belong to your other wallets outside of Fireblocks. If you intend to whitelist a wallet address under your control that is outside of Fireblocks, it should be added to the Internal Wallet entity. ## Contracts Contract Wallets are entities that hold whitelisted contract addresses. If you interact with smart contracts and want to restrict the approved list of contracts, you should whitelist the contract addresses under the Contract Wallet entity. # Whitelisting at Scale For businesses with a high volume of outgoing transactions in their fully automated workflows, whitelisting every external address can be cumbersome and disrupt the automation. To address this while maintaining high security standards, you can use one of the following approaches: 1. **API Key with Admin Permissions:** Create an API Key with admin permissions and set the required [approval group](https://support.fireblocks.io/hc/en-us/articles/29184265622556-Approval-groups) for the whitelisting operation to **1**.\ Pair the API Key with an API Cosigner and create a Cosigner Callback Handler server on your end, connecting it to your Cosigner.\ In this setup, you can call the whitelisting address APIs before each transaction while the approval will be fully automated by the API Key acting as part of the admin quorum.\ Additionally, each request will be sent to your callback handler, allowing you to programmatically decide whether the address should be whitelisted. 2. **Fireblocks One Time Address Feature:** Utilize the Fireblocks One Time Address (OTA) feature to send funds to a non-whitelisted address. This can be done by combining the appropriate Policy rules and potentially implementing internal validations on your callback handler. # Work with One Time Addresses The One Time Address (OTA) feature lets you transfer assets to non-whitelisted addresses in your Fireblocks workspace. Unlike whitelisted addresses, one-time addresses do not require approval by the Admin Quorum to transfer funds to them. However, enabling the OTA feature requires approval by the workspace Owner and the Admin Quorum. You can enable the OTA feature [via the Fireblocks Console](https://support.fireblocks.io/hc/en-us/articles/4409104568338-One-Time-Address-OTA-feature) or the [Fireblocks API](/reference/setotastatus). > [!WARNING] > > **This feature poses security risks!** We recommend [setting up a strict Policy](https://support.fireblocks.io/hc/en-us/articles/29211450299420-Policy-best-practices) before enabling it. You can set a variety of such Policy protective limitations, such as: > > * Restricting one-time address transfers only to certain preselected users or groups > * Requiring approval for one-time address transfers above a certain threshold # Whitelist IPs for API Keys Source: https://developers.fireblocks.com/docs/whitelist-ips-for-api-keys The IP allowlist (whitelist) enables you to restrict API calls to only accept specific IP addresses for each API key. Only allowing access to a select number of IP addresses can lower the chances of a security threat or malicious attack. If you have workspace owner permissions, you can follow [these instructions](https://support.fireblocks.io/hc/en-us/articles/4405980040210-Whitelisting-API-IP-Addresses) on the Help Center to add IP addresses to the allowlist. Alternatively, you can also submit a ticket to Fireblocks Support with the IP addresses of the machines running your API client(s) and their matching API key(s). # Why Screen Through Fireblocks Source: https://developers.fireblocks.com/docs/why-screen-through-fireblocks See where compliance screening runs in a Fireblocks transaction, what the API shows at each step, and which guide covers it. Fireblocks screens a transaction inside its own transaction path, so the outcome of your compliance policy is applied before an outgoing transaction is signed or incoming funds become available, with no enforcement logic for you to build. You set up checks and policies once; Fireblocks applies them to every matching transaction in the workspace. For the business case, see [Why Screen Through Fireblocks](https://support.fireblocks.io/hc/en-us/articles/30614328227100-Why-Screen-Through-Fireblocks) on the Help Center. ## How screening runs in a transaction ![Transaction lifecycle with screening outcomes](https://mintcdn.com/fireblocks-43c4b3ee/UNwBNbbfA7SOB2NL/images/docs/ic-transaction-lifecycle.jpg?fit=max\&auto=format\&n=UNwBNbbfA7SOB2NL\&q=85\&s=01d2555a02825b73a70402d860df2f19) | Step | What happens | In the API | Guide | | - | - | - | - | | 1. Transaction starts | You create an outgoing transaction, or Fireblocks detects an incoming one. | [`POST /transactions`](/api-reference/transactions/create-a-new-transaction) | n/a | | 2. Screening starts | Outgoing: right after you submit it, before Policies approval and signing. Incoming: at the first blockchain confirmation. | `status`: `PENDING_AML_SCREENING` | n/a | | 3. Checks run | For each enabled check, in order, the Trigger (Screening Policy) decides whether the transaction is screened. | `status` stays `PENDING_AML_SCREENING` | [What You Can Connect](/docs/what-you-can-connect) (order of checks), [Compliance Policies](/docs/compliance-policies) | | 4. Outcome applies | The Outcome (Post-Screening Policy) acts on each result. **Accepted** (Accept or Alert): the transaction continues; outgoing moves on to Policies approval and signing. **Rejected** (outgoing): never sent. **Frozen** (incoming): funds held. A Reject or Freeze ends screening; later checks do not run. | Accepted: `status` moves past `PENDING_AML_SCREENING`. Rejected: `REJECTED` with `subStatus` `REJECTED_AML_SCREENING`. Frozen: `subStatus` `AUTO_FREEZE`. | [Compliance Policies](/docs/compliance-policies) | | 5. Read the result | Any time after screening starts. In the Console: **Transaction History** > **Transaction Details**. | `transaction.status.updated` event; [`GET /screening/transaction/{txId}`](/api-reference/compliance/provides-all-the-compliance-details-for-the-given-screened-transaction) for the full result | [Compliance Events & Webhooks](/docs/compliance-events-webhooks), [Transaction Screening Operations](/docs/transaction-screening-operations) | | 6. Act on the transaction | Any time after screening starts: bypass a rejection, rescreen, freeze, or unfreeze. Freeze is API only. | Bypass creates a new transaction; the original stays `REJECTED`. | [Transaction Screening Operations](/docs/transaction-screening-operations) | ## Limitations * AML/KYT and Notabene do not screen these routes: vault-to-vault, vault-to-exchange, vault-to-Gas Station, Gas Station-to-vault, and non-custodial wallet to non-custodial wallet within the same workspace. Address Registry Screening, Bring Your Own Screening, Sumsub, and GTR screen every route. * Screening covers transactions in your Fireblocks workspace only, not activity outside it. ## On the Help Center For the business case and the Console view, see [Why Screen Through Fireblocks](https://support.fireblocks.io/hc/en-us/articles/30614328227100-Why-Screen-Through-Fireblocks). # Work with Fireblocks Gas Station Source: https://developers.fireblocks.com/docs/work-with-gas-station Most Ethereum Virtual Machine (EVM)-based blockchains require a fee, called *gas*, to be paid in their base asset to execute transactions of both the base asset and other tokens. Gas is deducted from the account that initiated the transaction. The Fireblocks Gas Station auto-fuels a vault account with the required base asset when auto-fueling is turned on for it. Using the Gas Station helps cases of Fireblocks customers who choose to use the omnibus account vault structure or similar. The gas comes from your *Gas Station tank*: a vault account or a [Wallet Pool](/docs/wallet-pools#use-cases) in your own workspace that you designate in **Settings** > **Gas Station tank**. You hold and manage the base assets in the tank, and the Gas Station transfers them from the tank to your auto-fueled vault accounts. See [Enable the Gas Station](/docs/enabling-the-gas-station-1). All transfers on Fireblocks, including those between vault accounts, occur on the blockchain. Therefore, you must pay gas fees when transferring funds from the deposit accounts to your omnibus vault account. The auto-fuel transaction is triggered whenever a withdrawal or deposit is detected to or from that vault account. Fireblocks checks the balance of the vault and transfers gas from the tank according to the [Gas Station parameters](/docs/configure-gas-station-values). The Gas Station removes the need to monitor the base asset levels of each vault account and manually transfer funds to them to cover future transaction fees when funds are swept to your central vault (omnibus) account, where they can be invested. > [!TIP] > > **Don't want vault accounts to hold gas at all?** With [gasless transactions](/docs/gasless-transactions), a relay pays the fee for token transfers instead. See [Gas Management](/docs/gas-management) to compare gasless transactions with Gas Station. # Supported networks Ethereum, all EVM-based networks available in your Fireblocks workspace, and TRON are supported. [See a complete list of Fireblocks Gas Station's supported networks with additional information](https://support.fireblocks.io/hc/en-us/articles/360016557960-Fireblocks-Gas-Station). # Common use cases for Gas Station Many retail businesses use the omnibus structure, making the Gas Station a helpful feature when many vault accounts are required to receive individual direct deposits from end clients. 1. In this case, you'd [monitor the Transaction status using webhooks](/reference/monitoring-transaction-status). 2. Then, upon receiving the `COMPLETED` transaction status, [trigger a sweep from the end-client vault account to your omnibus deposits vault account](/docs/sweep-funds). Other business types using the Gas Station include exchanges, lending desks, neobanks, and commercial and investment banks. # Workspace Comparison Source: https://developers.fireblocks.com/docs/workspace-environments Fireblocks has three types of workspaces. The workspace type you use will determine your capabilities, so knowing which type you're working in is important. * Developer Sandbox environments and Testnet workspaces are used for development and testing. * Mainnet workspaces are used to deploy into production (with real funds). Even though each workspace type has different capabilities, they all utilize similar APIs and SDKs, allowing you to develop and test your code against "lower" environments before deploying to your live production environment. # What is a workspace A workspace is a single, unique instance of the Fireblocks platform. It's where you and your organization manage vault accounts, assets, users, transactions, and Policies. Learn more in the [Fireblocks Object Model](/docs/object-model). Each workspace is a separate BIP32-HD wallet structure, derived from its own master key, with its own security and transaction Policies. API users and API keys belong to a specific workspace, so a Mainnet workspace requires a separate API key from a Testnet workspace. ## Find your workspace ID Each workspace has a unique identifier. Call [Get workspace](/api-reference/workspace/get-workspace) (`GET /workspace`) to return the `id` and `name` of the workspace your API key belongs to. The workspace identifier also appears in other places under different field names: * `tenantId` in [audit log](/docs/audit-logs-api) entries and v1 webhook payloads. * `workspaceId` in v2 webhook payloads. See the [Webhook v2 migration guide](/reference/webhook-v2-migration-guide). # Workspace Types | | Developer Sandbox | Testnet | Mainnet | | - | - | - | - | | **Purpose** | The [Fireblocks Developer Sandbox](/docs/quickstart) is a unique workspace built for developers to quickly start using Fireblocks APIs and SDKs. | Recommended for API testing. Before you go live, we recommend performing a test transaction on the Mainnet using both the Console UI and the API. | This is a full-production workspace. | | **Workflow testing** | Quick experimentation | Pre-production development & testing | Staging & production | | **Console URL** | sandbox.fireblocks.io | console.fireblocks.io | console.fireblocks.io | | **API URL** | sandbox-api.fireblocks.io | api.fireblocks.io/v1 | api.fireblocks.io/v1 | | **Derivation paths** | [Sandbox workspace exceptions](https://support.fireblocks.io/hc/en-us/articles/16097541672732-Path-differences-between-Sandbox-Testnet-and-Mainnet-workspaces) | [Testnet workspace exceptions](https://support.fireblocks.io/hc/en-us/articles/16097541672732-Path-differences-between-Sandbox-Testnet-and-Mainnet-workspaces) | [Fireblocks vault HD derivation paths](https://support.fireblocks.io/hc/en-us/articles/360014330819-Fireblocks-Vault-HD-derivation-paths) | | **Assets** | Testnet assets that are independent of Mainnet assets and operate on their own Testnet blockchains. | Testnet assets that are independent of Mainnet assets and operate on their own Testnet blockchains. | Mainnet / Real assets. | | **API** | Full API access with the most limited rate limits. Used for testing the API. | Full API access with limited rate limits. Used for testing the API. | Full API access with increased rate limits. Used for implementing the tested API configuration. Requires a separate API key from a Testnet workspace; this can be done with the same CSR file. | | **Policies** | Non-editable | [Fully editable](/docs/set-transaction-authorization-policy) | [Fully editable](/docs/set-transaction-authorization-policy) | | **API Co-Signer** | Only [Fireblocks-provided API Co-Signer](/cosigners/overview/use-communal-cosigner). | Fireblocks-provided or self-hosted API Co-Signer. | Self-hosted API Co-Signer. | | **API user limit** | Up to 5 users | Limited to the client setting | Limited to the client setting | | **Webhooks** | Available for testing | Available for testing | Available | | **Mobile access** | N/A | Fireblocks mobile app (iOS and Android) to sign and authorize transactions, approve external connections, and workspace changes. | Fireblocks mobile app (iOS and Android) to sign and authorize transactions, approve external connections, and workspace changes. | | **Fireblocks Network** | N/A | N/A | All Fireblocks Network connections are available. | | **Exchanges** | Limited exchange integration support (only testnets). | Limited exchange integration support (only testnets). We recommend setting Policy rules that apply to transactions on exchanges to accommodate test transactions that are processed approximately once per day. | All Fireblocks exchange connectivity is available. | | **Fiat** | Testnet only | Testnet only | All Fireblocks fiat integrations | | **DeFi/Web3** | Testnet protocols with WalletConnect Desktop integration. | Testnet protocols with WalletConnect Mobile and Desktop integrations. | Full support. Requires a specific policy implementation to enable. | | **AML** | N/A | N/A | Most assets are supported via Chainalysis or Elliptic. | | **Raw Signing enabled** | Yes | Requires a signed commercial agreement + Support enablement | Requires a signed commercial agreement + Support enablement | | **Support & Help Center** | [Developer Community support](https://community.fireblocks.com/) with Help Center access | Full Support with Help Center access | Full Support with Help Center access | # Mainnet Mainnet is a production-level blockchain network with cryptocurrency transactions being broadcast, verified, and recorded. Mainnets have a base asset that can be used as funds in transactions or to pay network fees. All assets on Mainnets have real-world monetary value. A Mainnet workspace is a fully developed and deployed environment that allows you and your organization to conduct your real-world financial operations on the Fireblocks platform. In Mainnet workspaces, you can securely: * Transfer digital assets * Add users * Create a Vault account or multiple * Whitelist new addresses * Connect to supported exchanges * Connect to other Fireblocks Network members * Configure MPC-CMP devices * Establish network connections > [!WARNING] > > **Moving to Mainnet workspaces:** When you’re ready to use your Mainnet workspace, there are a few things to keep in mind before you get started. > > * When testing new functions in Mainnet (production) environments, use small amounts first to confirm intended functionality before upscaling to larger amounts. > * Other than the users you add to both, your Mainnet and testnet workspaces are completely different workspaces. Your Mainnet workspace will require its own vault accounts and asset wallets, exchange and fiat account connections, policies, users, and more. > * You must define the Mainnet workspace’s Policy rules to work in accordance with the Mainnet workspace’s various users, user groups, sources, destinations, and assets. These parameters will likely be different from what you used in your testnet workspace. > * You must configure your Mainnet workspace to work with any API integrations and third-party accounts you want to carry over from your testnet workspace. This includes creating a new API key for and provisioning your API Co-Signer. Note that you can add Fireblocks Console users, adjust the Admin Quorum, and connect to exchange accounts, fiat accounts, and Fireblocks Network members before or after you configure your API integrations. # Testnet Testnet is a blockchain test network used by developers to run tests with the blockchain. Testnets operate on blockchains separate from their associated Mainnet. Testnet assets have no monetary value, and many of them reset periodically. A Testnet workspace is a testing-level environment that only allows you to use Testnet assets for integration and testing purposes. You can also perform many of the same actions you can in a Mainnet workspace without incurring real-world fees. In the Fireblocks Console, the "**Testnet Environment**" badge appears at the top of the page to help you identify your Testnet workspace. ## Limitations of Testnet workspaces The Testnet workspace will have certain conditions to protect you from losing real assets and compromising security while you test, such as: * Only Testnet assets and Testnet blockchains are supported (e.g., Ethereum Sepolia Testnet) * Simplified signing process that removes many security features in favor of simple setup and testing, such as no mobile signing application support and no client co-signer support * Limited exchange integrations since not all exchanges support Testnet blockchains and may not work as they would on Mainnet * Fiat integrations will only work on fiat test networks * DeFi and Web3 have limited blockchain and Web3 dApp test network support * The Fireblocks Network is not available * Transaction screening AML/KYT integrations are not available * Reduced API rate limits (production-level load testing is not available) # Developer Sandbox Sandbox workspaces are freely available to sign up for. These workspaces are geared towards quick development and experimentation. Here are some of the highlights of Sandbox environments: * When creating API users, the CSR certificate required to generate the private key is automatically created in the browser. * Access to the Developer Area API Monitoring feature that displays the workspace's API calls and errors over 24-hour and 7-day periods. * No mobile signing devices are needed since all transactions are auto-approved, reducing the complexity of trying out the Fireblocks API. ## Differences in Sandbox User Roles When creating new Console or API users in Developer Sandbox workspaces, there are only three workspace roles available: * Non-Signing Admin * Editor * Viewer In Sandbox workspaces, the Owner role is taken up by a backend service that manages auto-approvals to make experimentation and development easier. Therefore, the Non-Signing Admin replaces this role type and has the highest level of permissions, which are not present in Mainnet/Testnet workspaces, such as: * Creating and deleting users * Resetting 2FA for workspace users * Signing transactions (despite the "non-signing" in the role's name) For more information about workspace roles and permissions in production Mainnet and Testnet workspaces, [visit our Help Center](https://support.fireblocks.io/hc/en-us/articles/360012832959-User-roles). # Supported Mainnets, Testnets, and Assets Visit the [Supported blockchain networks](https://support.fireblocks.io/hc/en-us/articles/8993656344092-Supported-blockchain-networks) page in our Help Center to learn more about supported Mainnets, Testnets, and assets. # Write Canton Transactions Source: https://developers.fireblocks.com/docs/write-canton-transactions Make a Canton call to act on Canton, and answer an offer to respond to one. Both return the outgoing transaction; the on-chain outcome always arrives by webhook. > [!WARNING] > > Make a Canton call and Answer an offer are not yet available in production. The request and response shapes below are the committed contract, so you can build your integration against them now, but calls will fail until Fireblocks announces general availability. There are two ways to write to Canton: make a call to act on your own wallet or a contract, or answer an offer to respond to one. Both are `POST` requests that return `201` with the outgoing transaction that carries the action — never the on-chain result. Track that transaction with the [Canton Transaction Webhooks](/docs/canton-transaction-webhooks) or by [reading it directly](/docs/read-canton-transactions) to see how it resolved. ## Idempotency Both endpoints accept the optional `Idempotency-Key` header. Resending a request with the same key returns the original response instead of submitting a second time. See [API Idempotency](/reference/api-idempotency). > [!TIP] > > Set an idempotency key on every write. A network timeout does not tell you whether the request landed, and neither endpoint is safe to blindly retry without one. ## Make a Canton call See the full generated reference at [Make a Canton call](/api-reference/canton-beta/make-a-canton-call). ```http theme={"system"} POST /v1/operations/canton/calls ``` `type` selects the call and the shape of `payload`. An unknown or missing `type`, or a `payload` missing a required field, is a `400`. | `type` | Payload | v1 status | | - | - | - | | `DTCC_PARTICIPANT_ONBOARDING` | [ParticipantOnboardingPayload](#participantonboardingpayload) | `501` — capability exists, no handler yet | | `DTCC_END_INVESTOR_INVITE` | [EndInvestorPayload](#endinvestorpayload) | `501` — capability exists, no handler yet | | `DTCC_END_INVESTOR_INVITE_CANCEL` | [EndInvestorPayload](#endinvestorpayload) | `501` — no Canton capability | | `DTCC_END_INVESTOR_OFFBOARD` | [EndInvestorPayload](#endinvestorpayload) | `501` — no Canton capability | | `DTCC_ALLOW_LIST_ADD` | [AllowListPayload](#allowlistpayload) | `501` — no Canton capability | | `DTCC_ALLOW_LIST_REMOVE` | [AllowListPayload](#allowlistpayload) | `501` — no Canton capability | | `ALLOCATION_WITHDRAW` | [AllocationWithdrawPayload](#allocationwithdrawpayload) | Implemented | > [!NOTE] > > `ALLOCATION_WITHDRAW` is the only call implemented in v1. Every other `type` is a documented `501` today — the request shape below is the committed contract, not a preview of working behavior. ### ParticipantOnboardingPayload | Field | Required | Description | | - | - | - | | `vaultAccountId` | Yes | The vault account that acts as the participant. Its Canton party is derived for you. | | `asset` | Yes | `CANTON` or `CANTON_TEST`. | | `operator` | Yes | DTCC infra operator party id. | | `compliance` | Yes | DTCC compliance party id. | | `registrar` | Yes | DTCC registrar party id — co-signs the accept. | | `clientOnboarder` | Yes | DTCC client onboarder party id — co-signs the accept. | | `upgrader` | Yes | DTCC upgrader party id, the Model Upgrade Tool authority. Supplied by DTCC during off-chain registration alongside the other party ids. | | `expiresAt` | No | RFC 3339 date-time when the onboarding request expires if unanswered. | ### EndInvestorPayload Shared by invite, invite-cancel, and offboard — identical wire shape, different verb. | Field | Required | Description | | - | - | - | | `vaultAccountId` | Yes | The vault account whose Canton wallet acts here. | | `asset` | Yes | `CANTON` or `CANTON_TEST`. | | `endInvestor` | Yes | The end investor's Canton party id. | ### AllowListPayload | Field | Required | Description | | - | - | - | | `vaultAccountId` | Yes | The vault account whose Canton wallet acts here. | | `asset` | Yes | `CANTON` or `CANTON_TEST`. | | `wallets` | Yes | Canton party ids to add or remove. At least one. | ### AllocationWithdrawPayload | Field | Required | Description | | - | - | - | | `allocationTransactionId` | Yes | The Fireblocks transaction id of the outgoing response that created the allocation. The allocation is resolved from it — Canton contract ids are never accepted here. | Only accepted while that transaction sits at `status CONFIRMING` / `subStatus ALLOCATED`. See [The ALLOCATED lifecycle](/docs/read-canton-transactions#the-allocated-lifecycle). ```json theme={"system"} { "type": "ALLOCATION_WITHDRAW", "payload": { "allocationTransactionId": "9f2b7c14-8d55-4a02-b6e1-5c3f90a7d488" } } ``` A `201` returns the outgoing withdrawal transaction: ```json theme={"system"} { "transactionId": "c4d1f8a2-7e93-4b06-a815-3f9c2e0b6d47", "status": "SUBMITTED" } ``` `status` here is always `SUBMITTED` — it is the transaction's status at the moment of this response, not its outcome. Its resolution to `COMPLETED` or `CANCELLED` arrives later, by webhook or by re-reading the transaction. ### Responses | Status | Meaning | | - | - | | `201` | The outgoing transaction that carries the call. | | `400` | Unknown or missing `type`, a missing required payload field, or an invalid party id, asset, or expiry. | | `404` | No Canton wallet for this vault account and asset, or an unknown transaction id for this tenant. | | `409` | The target is not in a state this call can act on, or more than one candidate contract matched the wallet. | | `501` | This call type is not available in v1. | ## Answer an offer See the full generated reference at [Answer an offer](/api-reference/canton-beta/answer-an-offer). ```http theme={"system"} POST /v1/operations/canton/offers/{offerId}/responses ``` `offerId` is the Fireblocks transaction id of the incoming offer — the same `id` you read from `cantonDetails.offerResponse` or from the webhook's `data.id`. See [Decide whether an offer can be responded to](/docs/read-canton-transactions#decide-whether-an-offer-can-be-responded-to) before calling this. The body nests two layers of discrimination: `domain` selects `response`, and inside `response`, `responseType` selects the variant. Both layers are closed — sending an onboarding `responseType` under `domain: ALLOCATION` is a `400`, regardless of what the offer's own domain is. | `domain` | `responseType` | Arguments | v1 status | | - | - | - | - | | `ONBOARDING` | `DTCC_END_INVESTOR_ONBOARDING_ACCEPT` | None | Implemented | | `ONBOARDING` | `DTCC_END_INVESTOR_ONBOARDING_REJECT` | `reason` (required, 1–512 chars) | Implemented | | `ONBOARDING` | `TRADEWEB_COSIGNING_DELEGATION_ACCEPT` | None | Implemented | | `ONBOARDING` | `TRADEWEB_COSIGNING_DELEGATION_REJECT` | None | Implemented | | `ALLOCATION` | `ALLOCATION_ACCEPT` | None | Implemented | | `ALLOCATION` | `ALLOCATION_REJECT` | None | Implemented | | `TRANSFER` | `TRANSFER_ACCEPT` | None | `501` — domain declared, not available in v1 | | `TRANSFER` | `TRANSFER_REJECT` | None | `501` — domain declared, not available in v1 | > [!NOTE] > > `domain: TRANSFER` exists so the endpoint's contract covers every offer domain, not because transfer responses work today. Both `TRANSFER_ACCEPT` and `TRANSFER_REJECT` return `501` in v1. `reason` on a DTCC onboarding rejection is recorded on-chain, where the counterparty can read it. The Tradeweb rejection carries no `reason` — its DAR has nowhere on-ledger to record one, so the field does not exist on that variant. ```json theme={"system"} { "domain": "ONBOARDING", "response": { "responseType": "DTCC_END_INVESTOR_ONBOARDING_REJECT", "reason": "KYC not completed" } } ``` A `201` echoes the `responseType` back so you can correlate the response without re-reading it: ```json theme={"system"} { "transactionId": "5a1d8b0f-2c31-4c7e-9d0a-118e7b6c4a21", "status": "SUBMITTED", "responseType": "DTCC_END_INVESTOR_ONBOARDING_REJECT" } ``` > [!WARNING] > > This endpoint is the authority on answerability, not `availableResponses`. `availableResponses` states what the offer *type* accepts and does not change once the offer arrives — it does not tell you whether the offer is still open. This endpoint re-checks state and expiry on every call and answers `409` when either has moved. ### Responses | Status | Meaning | | - | - | | `201` | The outgoing transaction that carries the response. Its on-chain outcome arrives by webhook as a status update on this transaction. | | `400` | The `responseType` does not belong to the declared `domain`, or a required argument is missing. | | `404` | Unknown offer id for this tenant. | | `409` | The offer is not answerable — already answered, expired, or a response is in flight. | | `501` | No handler is implemented for this domain yet. | # x402 Agent Source: https://developers.fireblocks.com/docs/x402-agent Open-source Fireblocks-powered agent that automatically pays for x402-protected resources — available as an MCP server and a CLI. The x402 Agent is a TypeScript client for the **x402 payment protocol (v2)** that gives any autonomous agent the ability to pay for HTTP 402-protected resources using a Fireblocks vault. It handles the full payment loop: detecting the `402 Payment Required` response, constructing and signing the EIP-712 payment payload via Fireblocks, and retrying the original request with proof of payment. Gasless signing happens off-chain — the agent never submits an on-chain transaction itself. Source: [github.com/fireblocks/x402-agent](https://github.com/fireblocks/x402-agent) Ships in two forms: an **MCP server** for Claude Desktop, Cursor, and other MCP clients, and a **CLI** for scripting and testing payment flows from the terminal. Before production use, combine the four security layers in [Secure deployment](#secure-deployment) so the agent can only spend what you explicitly allow. ## Requirements * Node.js 18+ * Fireblocks API account with a vault configured * Sufficient token balance on the target network (for example, USDC on Base) ## How it works ### Payment sequence ``` Agent ──GET /resource──► Merchant server │ ◄─── 402 + PaymentRequired body Agent constructs EIP-712 payload, signs via Fireblocks, retries with PAYMENT-SIGNATURE header: Agent ──GET /resource──► Merchant server PAYMENT-SIGNATURE: │ ◄─── 200 + PAYMENT-RESPONSE header (tx hash, network, payer) ``` **The agent never calls the facilitator directly.** It sends signed payment headers to the merchant server; the merchant's x402 middleware calls the facilitator to verify and settle on-chain. 1. **Initial request** — Agent sends GET to the resource server. 2. **402 Payment Required** — Resource server returns a `PaymentRequired` JSON body with `accepts[]` payment options. 3. *(Optional)* **Integrity check** — Agent verifies the envelope against the facilitator's `did:web` key (see [Payment Instruction Integrity](#payment-instruction-integrity)). 4. **Select mechanism** — Agent picks the first supported `accepts[]` entry (preference: `eip3009` → `permit2`; overridable via `MECHANISM`). 5. **Build and sign EIP-712** — Agent constructs typed data locally and signs via Fireblocks. 6. **Retry with signature** — Agent resends the GET with a `PAYMENT-SIGNATURE` header. Resource server settles the payment and returns the resource with a `PAYMENT-RESPONSE` receipt header. Two round-trips total: the initial 402, then the retry with signature. ### Transfer mechanisms | Mechanism | Description | | - | - | | `eip-3009` | `TransferWithAuthorization` — native for USDC and other ERC-3009 tokens | | `permit2` | Uniswap Permit2 `PermitWitnessTransferFrom` — universal ERC-20 fallback | ERC-7710 delegation is not supported — Fireblocks cannot sign delegation payloads. Permit2 supports two schemes, chosen by the server in the 402 `accepts[].scheme` field: * **`exact`** — authorizes the merchant to pull **exactly** the amount in the 402 body. Recommended default. * **`upto`** — authorizes the merchant to pull **up to** the amount in the 402 body (the actual debit is decided by the facilitator at settlement time). Useful for variable-cost endpoints, but gives the merchant discretion within the cap. Pair with tighter Policy Engine rules if you use it. ### Payment Instruction Integrity The agent can verify that the `PaymentRequired` body wasn't tampered with between the facilitator and the client. * The facilitator emits a base64url-encoded envelope on `PaymentRequired.integrity` (also mirrored on the `X-402-Integrity` response header). * The envelope is signed ES256 (P-256) and the key is resolved via `did:web`. * Canonical bytes: `SHA-256( JCS({x402Version, accepts}) || "\n" || iat || "\n" || exp )`. * `resource.url`, `error`, and `extensions` are intentionally excluded from the signed slice. Set `VERIFY_INTEGRITY=true` for best-effort verification (warns if missing or invalid), or `REQUIRE_INTEGRITY=true` to abort when the envelope is absent or invalid. The `require_integrity` parameter on `x402_get_and_pay` enforces integrity for a single call and cannot relax env settings. ## Get started ```bash theme={"system"} git clone https://github.com/fireblocks/x402-agent cd x402-agent npm install cp .env.example .env # Edit .env with your Fireblocks credentials npm run build ``` Place your Fireblocks private key at the path referenced by `FIREBLOCKS_PRIVATE_KEY_PATH`. | Variable | Description | | - | - | | `FIREBLOCKS_API_KEY` | Fireblocks API key UUID | | `FIREBLOCKS_VAULT_ACCOUNT_ID` | Vault account to pay from | | `FIREBLOCKS_PRIVATE_KEY_PATH` | Path to Fireblocks RSA private key | | `MECHANISM` | Force a transfer mechanism: `eip3009` or `permit2` (default: `eip3009`, falls back to `permit2`) | | `VERIFY_INTEGRITY` | `true` to verify integrity envelopes when present | | `REQUIRE_INTEGRITY` | `true` to require a valid integrity envelope on every call | | `ALLOWED_MERCHANT_HOSTS` | Comma-separated allowed hosts; agent refuses any URL whose host isn't listed | | `REQUIRE_CONFIRM` | When `true`, MCP pay-tools refuse to pay unless called with `confirmed=true` | | `ALLOW_LOCAL_TARGETS` | When `true`, permit private / loopback URLs (local dev only; leave unset in production) | | `FIREBLOCKS_BASE_URL_ALLOWED_HOSTS` | Comma-separated additional Fireblocks API hostnames to trust beyond `*.fireblocks.io` | ## Usage ### MCP server The MCP server exposes four tools: | Tool | Description | | - | - | | `x402_get_and_pay` | Fetch a URL, automatically pay if 402 is returned, return the resource | | `x402_get_payment_info` | Inspect payment requirements for a URL without paying | | `http_get` | Generic GET (routes 402 responses to suggest x402 tools) | | `http_post` | Generic POST | Start the server (stdio): ```bash theme={"system"} npm start ``` #### Connect to Claude Desktop Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) — use **absolute paths**. Windows config lives at `%APPDATA%\Claude\claude_desktop_config.json`; Linux at `~/.config/Claude/claude_desktop_config.json`. ```json theme={"system"} { "mcpServers": { "x402-payment": { "command": "node", "args": ["/ABSOLUTE/PATH/TO/x402-agent/dist/mcp-server.js"], "env": { "FIREBLOCKS_API_KEY": "", "FIREBLOCKS_VAULT_ACCOUNT_ID": "", "FIREBLOCKS_PRIVATE_KEY_PATH": "/ABSOLUTE/PATH/TO/x402-agent/.secrets/your-key.key", "VERIFY_INTEGRITY": "true" } } } } ``` Restart Claude Desktop to pick up the server. #### Test with MCP Inspector ```bash theme={"system"} npx @modelcontextprotocol/inspector node dist/mcp-server.js ``` #### Example prompts ``` Use x402_get_and_pay to fetch http://localhost:3010/get-gold ``` ``` Use x402_get_payment_info to check what payment is required for http://localhost:3010/get-gold ``` #### x402\_get\_and\_pay options | Parameter | Type | Default | Description | | - | - | - | - | | `url` | string | required | URL to fetch | | `auto_pay` | boolean | `true` | Complete payment automatically | | `selection` | `"auto"` \| `"manual"` | `"auto"` | Option selection mode | | `option_index` | number | — | Explicitly pick a payment option by index | | `check_balance` | boolean | `true` | Query vault balance before picking an option | | `require_integrity` | boolean | `false` | Abort if Payment Instruction Integrity envelope is missing or invalid | **Auto selection** picks the best-funded option in preference order: `eip-3009` → `permit2`. Set `MECHANISM=permit2` to force a specific mechanism. **Manual selection** returns the full list of funded options when more than one is available. Re-call with `option_index=` to complete the payment. ### CLI Run a one-shot payment from the terminal: ```bash theme={"system"} npm run dev:cli ``` Or invoke directly with `npx` (no global install): ```bash theme={"system"} npx tsx src/index.ts ``` For a production build: ```bash theme={"system"} node dist/index.js ``` ## Secure deployment The agent never custodies keys — it asks Fireblocks to sign. Combine four controls so the agent can only spend what you've explicitly allowed, even if the host running it is compromised or an LLM driving it goes off-rails. ### Provision a scoped Fireblocks API user Grant only the minimum permissions the agent actually needs. Create a dedicated API user in the Fireblocks Console — don't reuse a workspace-admin key. See the official Fireblocks guides on [creating a new API user](https://support.fireblocks.io/hc/en-us/articles/4407823826194-Adding-new-API-Users) and [best practices for choosing user roles](https://support.fireblocks.io/hc/en-us/articles/5254222799900-Best-practices-for-choosing-user-roles). * **Role:** *Signer* (or any role limited to `Create Transaction` / signing operations). No policy edit, no user management, no workspace admin. * **Vault scope:** Restrict to a single dedicated vault used **only** for x402 spend. Keep its balance small — top it up as needed rather than parking your treasury there. * **IP allowlist:** Add the machine(s) that will run the agent. * **CSR / API key fingerprint:** Record both so you can rotate confidently. ### Store the API credentials securely The Fireblocks API key and its private signing key file are highly sensitive credentials. > [!WARNING] > > **API credentials must never be shared publicly** — not in commits, not in chat transcripts, not in screenshots. Store the private key file with restricted access (`chmod 600`), preferably outside the repo, and never bake it into a container image. The agent reads `FIREBLOCKS_API_KEY` (the API user ID, a UUID) and `FIREBLOCKS_PRIVATE_KEY_PATH` (path to the API user's private signing key). Treat both like production secrets. | Environment | Storage | | - | - | | Local dev | `.env` (gitignored) + key file in `.secrets/` (gitignored). Restrict file mode: `chmod 600 .secrets/*.key .env`. | | CI | Secrets injected as masked variables by the CI provider (GitHub Actions encrypted secrets, GitLab masked variables, etc.). Never echo them. | | Server / container | Secrets manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, HashiCorp Vault) mounted at runtime — not baked into the image. | | Kubernetes | `Secret` resource mounted as a file, ideally backed by External Secrets Operator pulling from a secrets manager. | Never commit `.env`, `.secrets/`, the API user PEM, or the rendered `.mcp.json` (the project's `.gitignore` already excludes these — keep it that way). ### Enforce human-in-the-loop signing via the Policy Engine This is the strongest control and the one most worth setting up. The [Fireblocks Policy Engine (TAP)](/docs/set-transaction-authorization-policy) gates every signing request at the Fireblocks side — not in agent code — so it survives a compromise of the agent host or its API key. See the [Fireblocks rule parameters guide](https://support.fireblocks.io/hc/en-us/articles/29211687914268-Policy-rule-parameters) for the full schema. Recommended rules for an x402 agent vault: * **Whitelist destinations.** Allow only the asset types and recipient addresses you expect (for example, USDC on Base to specific facilitator settlement contracts). Deny everything else. * **Per-tx amount cap** matched to a single x402 micropayment (for example, ≤ 1 USDC). * **Per-period velocity cap** (hourly / daily) matched to the agent's autonomous budget. * **Designate a manual signer / approver above a threshold.** Configure the policy to require a designated approver for any transaction above your auto-sign threshold; approvals are pushed to the Fireblocks mobile app and enforced cryptographically by the policy, not by trust in the agent code. This is the canonical Fireblocks human-in-the-loop pattern (same approach recommended by the [`fireblocks-mcp`](https://github.com/fireblocks/fireblocks-mcp) server). * **2-eyes for any change to the policy itself** so a compromised admin can't widen the rules silently. ### Constrain the agent at the application layer These are defense-in-depth — the Policy Engine is still the source of truth. Useful for autonomous-agent flows where the URL or LLM context can't be trusted: * **Fireblocks base-URL allowlist (on by default).** `FIREBLOCKS_BASE_URL` must be HTTPS under `*.fireblocks.io`. The Fireblocks SDK does not validate this URL itself, and its signed JWT only binds the request *path* — not the host — so an attacker who tampers with `FIREBLOCKS_BASE_URL` (via a poisoned `.env`, shell compromise, or CI env injection) could MITM the API channel and replay signed requests against the real Fireblocks within the 55s JWT validity window. For private / alternative endpoints, set `FIREBLOCKS_BASE_URL_ALLOWED_HOSTS=host1,host2` with the exact additional hostnames you trust. * **SSRF guard (on by default).** Every outbound URL is validated before any request is made: non-`http`/`https` schemes are rejected, and IP literals or DNS results in private / loopback / link-local / metadata-range space (including `169.254.169.254`, GCP `metadata.google.internal`, Alibaba `100.100.100.200`, IPv6 `fc00::/7` / `fe80::/10` / `::1`) are blocked. HTTP redirects (`maxRedirects: 0`) are disabled so a 3xx can't bypass the upfront check. Set `ALLOW_LOCAL_TARGETS=true` only for local development. * **`ALLOWED_MERCHANT_HOSTS`** — agent refuses any request whose host isn't in the comma-separated list. Recommended whenever the URL can come from an LLM or external feed. * **`REQUIRE_CONFIRM=true`** (MCP only) — `x402_get_and_pay` returns a payment preview and refuses to sign until called again with `confirmed=true`. * **`REQUIRE_INTEGRITY=true`** — refuse to sign 402 responses that fail Payment Instruction Integrity verification. * Prefer Permit2 **`exact`** over **`upto`** unless you specifically need variable-cost semantics. ## Troubleshooting | Issue | Cause | Solution | | - | - | - | | `no supported payment mechanism in accepts[]` | Server only offered ERC-7710 or another unsupported mechanism | Ensure the server offers `eip3009` or `permit2` | | `integrity check failed` | Envelope expired, `did:web` unresolvable, or signature invalid | Check facilitator clock skew and DID doc availability; relax to `VERIFY_INTEGRITY` if not enforcing | | `ERROR_TYPED_MESSAGE_TO_PROTECTED_7702_CONTRACT` | Fireblocks policy blocked the signature | Add a Typed Message Policy in Fireblocks that allows this typed data | | Payment expired | Too slow between 402 and retry | Re-request to get a fresh 402; reduce Fireblocks signing latency | | Invalid signature | Wrong domain/types/message | Verify `eip712_name` / `version` from `accepts[].extra`, and that chainId matches | ## References * [EIP-3009: Transfer With Authorization](https://eips.ethereum.org/EIPS/eip-3009) * [EIP-712: Typed structured data hashing and signing](https://eips.ethereum.org/EIPS/eip-712) * [Permit2](https://github.com/Uniswap/permit2) * [Fireblocks Web3 Provider](https://github.com/fireblocks/fireblocks-web3-provider) * [Repository source layout](https://github.com/fireblocks/x402-agent) — `src/index.ts` (CLI), `src/mcp-server.ts` (MCP), `src/HttpX402Service.ts` (payment logic), `src/FireblocksService.ts` (Web3 provider), `src/IntegrityVerifier.ts` (integrity checks) # Fund and Control Agent Spending Source: https://developers.fireblocks.com/docs/x402-agent-spending-controls Fund AI agents from Fireblocks vaults and control what they spend at company scale: budgets, per-agent isolation, Policy-enforced limits, and a full audit trail. When an AI agent pays for x402-protected resources, it spends real on-chain value from a Fireblocks vault. This page covers controlling that spend at company scale: how to fund agents, set what each one is allowed to spend, run many of them safely, and reconcile every payment. It builds on the [x402 Agent](/docs/x402-agent) and its [Secure deployment](/docs/x402-agent#secure-deployment) controls, and frames them for finance and security teams who need oversight across a fleet. To publish the services that agents pay for, see [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog). The foundation is that the agent never holds private keys. It asks Fireblocks to sign each payment authorization, so every spend passes through controls you set in Fireblocks, not controls the agent enforces on itself. This gives a company the same oversight over autonomous agent spending that it has over human procurement: a per-purchase limit, a budget, an approved-vendor list, and an approval step for large amounts. > [!NOTE] > > **The agent enforces no spending limit of its own.** The x402 Agent has no per-call amount cap and no per-period velocity cap in its code. It will optionally refuse a payment when the vault balance is too low, but every real spending limit is enforced by Fireblocks Policy rules. Treat Policy rules as the source of truth, not the agent. ## The control plane Controlling agent spend at a company level rests on two decisions you make in Fireblocks, plus the agent's own guardrails: * **How much each agent is funded.** A vault's balance is a hard ceiling on what the agent can ever spend. * **What Policy rules allow.** Per-transaction caps, velocity limits, destination allowlists, and human-approval thresholds, enforced at Fireblocks so they hold even if an agent's host is compromised. * **Application-layer guardrails.** Defense-in-depth settings in the agent: merchant allowlists, confirmation gates, and integrity checks. The per-agent setup (scoped API user, secret storage, Policy rules, and guardrails) is documented step by step in [Secure deployment](/docs/x402-agent#secure-deployment). This page focuses on what a company layers on top: funding, running a fleet, and auditing spend. ## Fund an agent Funding an agent means transferring the spend asset (commonly USDC) into the dedicated vault account that agent's API user is scoped to, in the Fireblocks Console or through the API, exactly as you would fund any vault. Two practices keep funding safe: * **Keep the balance small.** Fund the vault to the agent's working budget, not your treasury. Because the balance is a hard ceiling, a small balance caps the worst-case loss if the agent or its host is compromised, independent of any Policy rule. * **Top up on a schedule.** Refill to the working budget periodically rather than pre-loading a large amount. Combined with a Policy velocity cap, this bounds the spend rate even between top-ups. The agent pays by signing an off-chain EIP-712 authorization, so it spends no gas at signing time. On-chain settlement and its gas are handled by the merchant's facilitator, not the buyer's vault. The agent's vault only needs the spend asset itself. ### How the balance check bounds a single payment Before paying, the agent can confirm the vault holds enough of the asset. The `x402_get_and_pay` tool and the CLI accept a `check_balance` flag (default `true`): * When `true`, the agent queries the vault balance for each candidate asset and only selects a funded option. If nothing is funded, it does not sign. * When `false` (optimistic mode), the agent skips the query and attempts the best-matching option regardless. The balance check is a convenience that avoids signing a payment the vault cannot cover. It is not a spending limit: a funded vault pays whatever the merchant quotes unless a Policy rule says otherwise. Spending limits belong in [Policy rules](/docs/x402-agent#enforce-human-in-the-loop-signing-via-the-policy-engine). ## Set what an agent can spend Two settings turn a funded vault into a budgeted one, both enforced at Fireblocks rather than in agent code: * **Per-transaction amount cap.** Match a single x402 micropayment, for example 1 USDC or less. Any signing request above the cap is rejected. * **Per-period velocity cap.** An hourly or daily ceiling matched to the agent's autonomous budget, so total spend is bounded regardless of how many calls the agent makes. Pair these with a **destination allowlist** (only expected assets and recipient addresses) and a **manual approver above a threshold** for high-value actions. Approvals are pushed to the Fireblocks mobile app and enforced cryptographically by the Policy rule, not by trust in the agent code. The full rule set and configuration steps are in [Enforce human-in-the-loop signing](/docs/x402-agent#enforce-human-in-the-loop-signing-via-the-policy-engine). For autonomous flows where the target URL or the model's context cannot be trusted, also enable the agent's [application-layer guardrails](/docs/x402-agent#constrain-the-agent-at-the-application-layer): a merchant-host allowlist (`ALLOWED_MERCHANT_HOSTS`), a confirmation gate (`REQUIRE_CONFIRM`), integrity enforcement (`REQUIRE_INTEGRITY`), and a preference for the Permit2 `exact` scheme over `upto`. > [!WARNING] > > On-chain transfers authorized by an agent are irreversible. A misconfigured Policy rule, an over-funded vault, or an over-broad destination allowlist can result in permanent loss of funds. Test on testnets, fund vaults conservatively, and review Policy rules before pointing an agent at mainnet. ## Run a fleet of agents The per-agent model composes directly into a company-wide setup. Give each agent, or each team's agent fleet, its own dedicated vault account and [scoped API user](/docs/x402-agent#provision-a-scoped-fireblocks-api-user), then apply a consistent set of Policy rules across them: * **Per-agent isolation.** Separate vaults and API users mean a compromised agent cannot touch another agent's funds, and you can revoke or rotate one agent's credentials without affecting the rest. * **Budgets as funding plus velocity caps.** An agent's effective budget is the combination of how much you fund its vault and the per-period velocity cap in its Policy rules. Set both deliberately, and the two together cap spend even if one is misjudged. * **Central policy governance.** Manage Policy rules centrally and require two approvers for any change to a policy, so spending authority for every agent is governed in one place and cannot be widened silently. This structure lets a finance team reason about a fleet the way it reasons about a set of corporate cards: each agent has its own limit and its own ledger, and the rules that govern them all are changed under dual control. ## Monitor and audit spending Every agent payment leaves two records you can reconcile: * **On-chain.** Each settlement is a token transfer on the network, giving a permanent, independently verifiable audit trail. Vault activity is also visible in the Fireblocks Console. * **Facilitator-side.** The merchant's facilitator persists every payment with its status, amount, asset, and transaction hash. If your company also operates the facilitator, inspect these with `x402 payments list` and the [management API](/docs/x402-facilitator-operations#inspecting-payments). Reconciling the two confirms that what an agent signed matches what settled on-chain, and gives finance a per-agent spend ledger drawn from authoritative sources rather than from the agent's own reporting. ## What to read next * [x402 Agent](/docs/x402-agent) — the agent library, its MCP server and CLI, environment variables, and the full [Secure deployment](/docs/x402-agent#secure-deployment) control set. * [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog) — declaring and publishing the services agents pay for. * [Set Policies](/docs/set-transaction-authorization-policy) — the Fireblocks Policy Engine that enforces agent spending limits. # x402 Concepts and Glossary Source: https://developers.fireblocks.com/docs/x402-concepts The core ideas and vocabulary behind x402 payments on Fireblocks: the 402 handshake, facilitator, schemes, transfer mechanisms, EIP-712, and settlement. This page explains how an x402 payment works and defines the terms used across the Fireblocks x402 docs. Read it once and the rest of the documentation reads clearly. If you are deciding where to start, see [Get Started with x402](/docs/x402-getting-started). ## The 402 handshake x402 revives the long-reserved HTTP `402 Payment Required` status code and gives it a job. A single payment follows the same four-step handshake every time: 1. A caller makes a normal HTTP request to a resource it wants. 2. The server responds **`402 Payment Required`** with a JSON quote: how much, in which asset, on which network, and to which address. 3. The caller signs a one-time authorization to pay exactly that amount, then retries the same request with a `payment-signature` header. 4. The server verifies the signature, settles the transfer on-chain, and returns the resource. The authorization covers a single transfer of a specific amount. There is no long-lived approval, no subscription, and no payment account to maintain. The caller signs off-chain and spends no gas at signing time; the on-chain settlement happens on the server side. ```mermaid theme={"system"} sequenceDiagram participant C as Caller (client or agent) participant S as Resource server participant F as Facilitator C->>S: GET /resource S-->>C: 402 Payment Required (quote) Note over C: Sign authorization C->>S: GET /resource + payment-signature S->>F: verify, then settle F-->>S: settled on-chain S-->>C: 200 OK + resource ``` ## Roles **Resource server (merchant).** The server that owns the paid resource. It returns the `402`, then calls the facilitator to verify and settle. In the Fireblocks model it keeps serving its own traffic; payments are a side call. **Facilitator.** The service that verifies a payment signature off-chain and settles the transfer on-chain. The Fireblocks facilitator settles through your Fireblocks vault, so raw private keys never leave Fireblocks. See [Facilitator Overview](/docs/x402-facilitator-overview). **Caller, client, or payer.** Whatever makes the request and signs the payment. It can be a browser, a script, or an AI agent. The [x402 Agent](/docs/x402-agent) is a payer that signs through a Fireblocks vault. **Buyer and seller.** The two sides of the market. Sellers run a facilitator and publish products; buyers run agents that pay for them. See [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog) and [Fund and Control Agent Spending](/docs/x402-agent-spending-controls). ## Payment terms **Product.** A payment-gated endpoint on the facilitator, with a price, accepted assets, and metadata. The unit a catalog is built from. See [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog). **`PaymentRequired` body.** The JSON the server returns with a `402`. It carries an `accepts[]` array, one entry per way to pay. Each entry names a scheme, network, amount, asset, and the recipient address (`payTo`). See the [Protocol Reference](/docs/x402-protocol-reference). **Scheme.** How the amount is interpreted. `exact` charges precisely the quoted amount. `upto` authorizes a ceiling and lets the facilitator settle any amount up to it, which suits metered or usage-based billing. **`payTo`.** The on-chain address that receives the payment, derived from the merchant's Fireblocks vault. **Settlement.** The on-chain step where the authorized transfer actually moves tokens. The Fireblocks facilitator settles with a `CONTRACT_CALL` transaction through your vault. **Asset.** The token used to pay, identified to Fireblocks by a native asset ID (for example, `USDC_BASE`). Stablecoins like USDC are the common choice. See [Supported Networks and Assets](/docs/x402-networks-assets). ## Transfer mechanisms A transfer mechanism is the on-chain method that moves the tokens once a payment is authorized. It determines what the payer signs and how settlement executes. The Fireblocks facilitator supports four: | Mechanism | What it is | When it applies | | - | - | - | | `eip-3009` | The token's native `transferWithAuthorization()`, signed once. | USDC and other ERC-3009 tokens. The default for USDC. | | `permit2` | Settlement through the Uniswap-deployed Permit2 contract. | Any ERC-20, regardless of whether it implements 3009. | | `upto-permit2` | Permit2 with a signed ceiling; the facilitator picks the final amount. | Metered or variable-cost billing. | | `erc7710` | Smart-account delegation via the MetaMask Delegation Framework. | Payers using smart accounts or EIP-7702-upgraded EOAs. | The [x402 Agent](/docs/x402-agent) signs `eip-3009` and `permit2`. It does not sign `erc7710`, because Fireblocks cannot sign delegation payloads. For how a merchant chooses and offers mechanisms, see [Transfer mechanisms](/docs/x402-facilitator-integration#transfer-mechanisms). ## Cryptography and standards **EIP-712.** The Ethereum standard for signing structured, human-readable data. An x402 payment authorization is an EIP-712 typed message, so the payer signs a clearly defined object (amount, recipient, deadline) rather than opaque bytes. See [EIP-712](https://eips.ethereum.org/EIPS/eip-712). **EIP-3009 (`TransferWithAuthorization`).** A token standard that lets a holder authorize a transfer with a signature, which a third party then submits. USDC implements it. This is what the `eip-3009` mechanism uses. See [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009). **Permit2.** A Uniswap-deployed contract that enables signature-based transfers for any ERC-20, used by the `permit2` and `upto-permit2` mechanisms. See [Permit2](https://github.com/Uniswap/permit2). **Nonce.** A unique value included in each authorization so a signature cannot be replayed for a second payment. **Payment Instruction Integrity (PII).** An optional extension where the facilitator signs the payment-critical part of the `402` body (the `accepts[]` entries) with an ES256 key published at a `did:web` address. A wallet or agent fetches that key and refuses to sign if the instructions were altered in transit. See [Payment Instruction Integrity](/docs/x402-facilitator-operations#payment-instruction-integrity). ## Networks and money **Network.** The EVM-compatible blockchain a payment settles on, identified in the CAIP-2 form `eip155:` (for example, `eip155:8453` for Base). x402 supports many networks; cost and finality differ across them. **Testnet and mainnet.** Test networks (Base Sepolia, Ethereum Sepolia) use valueless tokens from faucets and are where you evaluate x402. Mainnet moves real value. The Fireblocks facilitator is testnet-only unless you explicitly opt in. See [Supported Networks and Assets](/docs/x402-networks-assets). **Gas.** The on-chain transaction fee paid to settle. Because settlement cost varies by network (cents on Base, more on Ethereum mainnet), the network you accept affects the economics of small payments. See [Pricing modes](/docs/x402-facilitator-integration#pricing-modes). ## Discovery **Discovery API.** A public catalog the facilitator exposes at `GET /api/discovery/resources`, listing products marked discoverable so agents can find them. See [Publish to the discovery catalog](/docs/x402-facilitator-product-catalog#publish-to-the-discovery-catalog). **Bazaar.** The Coinbase-run directory of x402-enabled services. The Fireblocks discovery response follows the same shape, so Bazaar-aware agents can read your catalog. See [x402 Ecosystem and Interoperability](/docs/x402-ecosystem). ## What to read next * [Get Started with x402](/docs/x402-getting-started) — choose your path: selling, buying, or both. * [x402 Quickstart](/docs/x402-quickstart) — see the handshake run end to end on a testnet. * [x402 Protocol Reference](/docs/x402-protocol-reference) — the exact `402` body, headers, and error codes. # x402 Ecosystem and Interoperability Source: https://developers.fireblocks.com/docs/x402-ecosystem How the Fireblocks x402 facilitator and agent interoperate with the wider x402 ecosystem: the open protocol, the Bazaar directory, and agent payment standards. x402 is an open protocol, not a Fireblocks product. The Fireblocks facilitator and agent implement the same standard that other tools across the ecosystem do, so they interoperate with x402 services and directories you do not control. This page maps the landscape and where Fireblocks fits. For the protocol mechanics, see [x402 Concepts and Glossary](/docs/x402-concepts). ## The open protocol The x402 specification lives at [x402.org](https://x402.org) and defines the `402` handshake, the `PaymentRequired` body, and the roles of client, resource server, and facilitator. Any compliant facilitator can settle a payment any compliant client signs, on any supported network. The Fireblocks facilitator follows x402 v2; see the [Protocol Reference](/docs/x402-protocol-reference) for the exact message shapes it emits and accepts. What Fireblocks adds is the settlement and custody layer underneath the protocol. Payments settle through a Fireblocks vault, raw private keys never leave Fireblocks, and spending is governed by Fireblocks Policy rules. The protocol stays standard; the key management and controls are the Fireblocks contribution. ## Discovery and the Bazaar The [Coinbase x402 Bazaar](https://docs.cdp.coinbase.com/x402/bazaar) is a directory where agents discover x402-enabled services across the ecosystem. The Fireblocks facilitator's discovery API (`GET /api/discovery/resources`) returns resources in the same shape the Bazaar uses, so a Bazaar-aware agent can read a Fireblocks facilitator catalog with the same code. You decide which products are discoverable; publishing a product exposes only its public quote, never your vault or keys. See [Publish to the discovery catalog](/docs/x402-facilitator-product-catalog#publish-to-the-discovery-catalog). ## Agents and the MCP ecosystem x402 is designed for agent-to-service commerce, and the agent tooling reflects that. The [x402 Agent](/docs/x402-agent) ships as a Model Context Protocol (MCP) server, so any MCP-compatible client (Claude Desktop, Cursor, and others) can pay for resources through a Fireblocks vault by calling a tool. Because the payment side is standard x402, the same agent pays both services that settle through Fireblocks and third-party x402 endpoints. The broader ecosystem is also converging on standards for how agents authorize payments, such as Google's Agent Payments Protocol (AP2), which can use x402 as a settlement rail. As these standards stabilize, an x402 facilitator remains the component that verifies and settles, regardless of how the agent obtained its authorization. ## Where Fireblocks differs The Fireblocks implementation makes choices an operator should know when interoperating: * **Custody.** Settlement runs through a Fireblocks vault using MPC-based key management, so the facilitator process holds no signing material. Many other facilitators sign with a hot key the operator manages directly. * **Transfer mechanisms.** The facilitator settles `eip-3009`, `permit2`, `upto-permit2`, and `erc7710`. The agent signs `eip-3009` and `permit2` only, because Fireblocks cannot sign ERC-7710 delegation payloads. A service that offers only ERC-7710 cannot be paid by the Fireblocks agent, though it can be settled by the Fireblocks facilitator. * **Controls.** Spending and settlement are governed by Fireblocks Policy rules rather than application code, which is how a company bounds autonomous agent spend. See [Fund and Control Agent Spending](/docs/x402-agent-spending-controls). > [!NOTE] > > The x402 protocol specification is independent of Fireblocks and may evolve separately. Third-party components in the ecosystem (other facilitators, directories, token contracts, and delegation frameworks) carry their own risks. Review the facilitator's [`DISCLAIMER.md`](https://github.com/fireblocks/x402-facilitator/blob/main/DISCLAIMER.md) before relying on them in production. ## What to read next * [Get Started with x402](/docs/x402-getting-started) — choose whether you are selling, buying, or both. * [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog) — publish services into the discovery ecosystem. * [x402.org](https://x402.org) — the open protocol specification. # Integration Source: https://developers.fireblocks.com/docs/x402-facilitator-integration Integrate the Fireblocks x402 Facilitator from your merchant server. Covers the payment processing API, transfer mechanisms, and pricing modes. This page covers how to wire the facilitator into a merchant server: the middleware pattern, the four endpoints of the payment processing API, when to settle, the four transfer mechanisms, and how to price products. For the high-level concept and a quick-start bootstrap, see the [Overview](/docs/x402-facilitator-overview). ## Integrating from your merchant server Your server keeps serving its own traffic. You add middleware (Express, Next.js, Hono, or any equivalent — the same pattern as the Coinbase x402 SDK packages) that does two things: 1. On every request, inspect the incoming path. If the path is payment-gated and lacks a `payment-signature` header, return **402** with a JSON body. Your middleware builds this from a product declared in your config. 2. On a request that carries a `payment-signature` header, call this facilitator's `POST /api/payments/verify` to check the signature, then `POST /api/payments/settle` to run the on-chain transaction. If both succeed, serve your resource. The request your middleware sends to the facilitator: ```bash theme={"system"} curl -X POST https://facilitator.example.com/api/payments/verify \ -H "Authorization: Bearer $MERCHANT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "paymentPayload": { /* the client-signed payload */ }, "paymentRequirements": { /* what you advertised in the 402 */ } }' ``` `POST /api/payments/settle` takes the same body shape. The facilitator never sees the end-user's HTTP request. It only handles the cryptographic verification and the Fireblocks `CONTRACT_CALL`. ## The payment processing API These are the endpoints your merchant server (or any machine client) hits once you have given it an API key. All endpoints under `/api/payments/*` are authenticated with the merchant API key you minted via `x402 keys create` or `POST /api/admin/tokens` — except `/api/payments/supported`, which is public. ### `GET /api/payments/supported` Public discovery endpoint. Returns the schemes, networks, and extensions this facilitator accepts. ### `POST /api/payments/create` Returns a 402 `PaymentRequired` payload for a product without being gated behind a 402 flow. Useful for clients that want the quote before making the real request. ```bash theme={"system"} curl -X POST https://facilitator.example.com/api/payments/create \ -H "Authorization: Bearer $MACHINE_KEY" \ -H "Content-Type: application/json" \ -d '{"product_id":"prod_7Lm3nOp"}' ``` ### `POST /api/payments/verify` Validates an EIP-712 signature off-chain without moving funds. Use this as a cheap pre-flight to confirm the signature is well-formed, the authorization covers the advertised price, and the payer signed the expected payload. ```bash theme={"system"} curl -X POST https://facilitator.example.com/api/payments/verify \ -H "Authorization: Bearer $MERCHANT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "paymentPayload": { /* signed payload from the client */ }, "paymentRequirements": { /* what you advertised in the 402 */ } }' ``` Response shape: ```json theme={"system"} { "isValid": true, "payer": "0x...", "invalidReason": null } ``` ### `POST /api/payments/settle` Verifies the signature, then submits the on-chain transfer through Fireblocks. The request body matches `/verify`. The facilitator persists the Fireblocks transaction ID before polling for confirmation, so a process restart never loses track of an in-flight settlement. ```json theme={"system"} { "success": true, "txHash": "0x...", "networkId": "base-mainnet" } ``` ### When to settle Your middleware controls the order: * **Optimistic settlement** — serve the response first, call `/settle` in the background. Simpler, preferred when the resource is idempotent or cheap. * **Settle-first** — call `/settle` synchronously before serving. Safer for one-shot, expensive deliveries where a refund is not practical. `/api/payments/settle` is a single atomic operation on the facilitator side. The choice of when to invoke it is a middleware concern, not a facilitator one. ## Transfer mechanisms Every asset registered on the facilitator declares a `transfer_mechanism`. The mechanism controls how signatures are validated and how the on-chain transfer is executed. A product can offer the same asset under multiple mechanisms; the facilitator emits one `accepts[]` entry per pricing row in the 402 response. | Mechanism | Use it when | | - | - | | `eip-3009` | The token natively supports `transferWithAuthorization()` (notably USDC). Single transaction, no helper contract. The default for USDC. | | `permit2` | You need a universal ERC-20 path that does not depend on the token implementing 3009. Settled through the Uniswap-deployed [Permit2](https://github.com/Uniswap/permit2) contract via the `x402ExactPermit2Proxy`. | | `upto-permit2` | The merchant needs to charge a variable amount within a client-signed ceiling — metered API calls billed at the end, for example. The facilitator picks the actual charge up to the signed maximum. | | `erc7710` | The payer is a smart account (or an EOA upgraded via [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)) authorizing through the MetaMask Delegation Framework. Settled with `DelegationManager.redeemDelegations()`. | > [!WARNING] > > `upto-permit2` lets the facilitator select the actual charge amount up to the user's signed maximum. Your pricing logic is solely responsible for staying within that ceiling and for charging amounts the payer would expect. There is no protocol-level cap below the signed maximum. ### Picking a mechanism * If you accept USDC and your customers hold standard USDC, choose `eip-3009`. * If you accept other ERC-20s, or you want a single code path that works across tokens, choose `permit2`. * If your billing model is metered or usage-based, choose `upto-permit2`. * If your customers transact from smart accounts or want delegation-based authorization, choose `erc7710`. The facilitator enforces the chosen mechanism on `/verify` and `/settle`. A client cannot upgrade from `eip-3009` to `permit2` by shipping a different payload shape — if the resolved mechanism is not in the product's `pricing[]`, the request returns `400 unsupported_scheme`. Requests missing `extra.productId` (that is, requests not originated through `/api/payments/create`) are rejected. ## Pricing modes Each product has a `pricing[]` table listing one entry per accepted asset. Each entry is one of: * **Native-denomination** — `amount` is set. The client is charged exactly `amount` base units of that asset, regardless of exchange rates. Useful when you bill in crypto directly. * **USD-converted** — `amount` is null. The product's `usd_price` (fractional dollars) is converted to this asset at request time using a `PriceProvider`. ### Mixing both on one product A product can mix native and USD-converted entries in the same `pricing[]`: ```jsonc theme={"system"} { "usd_price": 0.10, "pricing": [ { "asset_id": "USDC_BASE" }, // convert $0.10 → USDC { "asset_id": "USDC_POLYGON", "amount": 90000 }, // override: pay 0.09 USDC on Polygon { "asset_id": "ETH_BASE" } // convert $0.10 → ETH at the current rate ] } ``` ### Multiple transfer mechanisms on the same asset Each pricing row accepts an optional `transfer_mechanism` override. When present, it takes precedence over the asset's own `transfer_mechanism` — so a single asset can be offered under several mechanisms at once, and the 402 emits one `accepts[]` entry per row. ```jsonc theme={"system"} { "usd_price": 0.10, "pricing": [ { "asset_id": "USDC_BASE" }, // inherit → eip-3009 { "asset_id": "USDC_BASE", "transfer_mechanism": "permit2" }, // same asset, permit2 { "asset_id": "USDC_BASE", "transfer_mechanism": "erc7710" } // same asset, MDF delegation ] } ``` ### PriceProvider The bundled `PriceProvider` stack is a `CompositePriceProvider` that tries in order: 1. `StableOnlyPriceProvider` — any asset marked `stable: true` quotes at 1:1 USD. No network call. 2. `CoinGeckoPriceProvider` — queries CoinGecko `/simple/price` using the asset's `price_symbol` (for example, `ethereum`, `weth`, `matic-network`). 30-second in-memory cache; serves stale prices for up to 5 minutes if the live call fails. Pro-tier users can set `COINGECKO_API_KEY`. Swap for Pyth, Chainlink, or a merchant-owned oracle by implementing the `PriceProvider` interface (`src/services/pricing/PriceProvider.ts`) and passing it into `PricingService` at startup. Assets that cannot be priced at all (no `stable` flag, no `price_symbol`, live provider down) are **dropped from the 402 response**, not rejected — other accepted assets remain available to the client. ### Gas cost — placeholder today Ethereum mainnet settlement can cost $1 or more in gas; the same transaction on Base is fractions of a cent. A merchant who quotes "$0.10" should not accept mainnet payments at \$0.10, or they lose money to settlement gas. The `GasCostEstimator` interface and `NoopGasCostEstimator` are wired into the pricing pipeline today but return `0` on every chain. When you are ready to enable chain-aware pricing, implement `GasCostEstimator.estimate(chainId, mechanism)` (using your RPC, Chainlink gas oracles, or Fireblocks fee estimates) and pick a `GasCostPolicy`: * `ignore` — the current default. * `add-to-quote` — gross up the client's charge so merchant revenue is constant. * `reject-if-above-pct` — drop uneconomic chains from `accepts[]`. See `src/services/pricing/GasCostEstimator.ts` in the repo for the interfaces. ## What to read next * [Operating and production](/docs/x402-facilitator-operations) — the config file, auth model, management API, CLI reference, Payment Instruction Integrity, production deployment, and operator responsibilities. # Operating and Production Source: https://developers.fireblocks.com/docs/x402-facilitator-operations Operate and deploy the Fireblocks x402 Facilitator: config file, auth model, management API, CLI, Payment Instruction Integrity, production deployment, and operator risk. This page covers everything beyond the merchant integration: the config file, the two auth surfaces, the management API, the CLI, optional Payment Instruction Integrity, production deployment, multiple-merchant hosting, and operator responsibilities. For the protocol overview and quick start, see the [Overview](/docs/x402-facilitator-overview). For the merchant-side integration and pricing, see [Integration](/docs/x402-facilitator-integration). ## The config file Everything non-runtime lives in `config/facilitator.json`. The file is loaded and validated at boot; changes require a server restart. ```jsonc theme={"system"} { "tenant_id": "acme", "default_configuration_id": "default", "assets": [ { "asset_id": "USDC_BASE", "blockchain_id": "0318d40f-7709-4f10-b980-11f3abaf31ac", "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "decimals": 6, "chain_id": 8453, "eip712_name": "USD Coin", "eip712_version": "2", "transfer_mechanism": "eip-3009", "stable": true, "price_symbol": null } ], "configurations": [ { "configuration_id": "default", "public_host": "https://pay.myservice.com", "fireblocks": { "api_key": "...", "api_secret_path": "./secrets/fireblocks.pem", "receiver_vault": "0", "base_url": "https://api.fireblocks.io", "deposit_address_cache": { "USDC_BASE": "0xMerchantVaultAddress" } }, "api_keys": [ { "key_id": "ak_...", "hash": "sha256:...", "scopes": ["process-payments"], "label": "agent" } ], "products": [ { "product_id": "prod_...", "name": "Premium Data", "endpoint": "/premium", "scheme": "exact", "usd_price": 0.10, "pricing": [ { "asset_id": "USDC_BASE", "amount": null } ], "description": null, "mime_type": "application/json", "category": null, "is_discoverable": false } ] } ] } ``` A few things to know: * `configurations[]` — one facilitator process can host many merchants. Each configuration has its own Fireblocks credentials, API keys, and product catalog. Clients are routed to the right configuration by the `Host` header matching `public_host`. * `asset_id` is the Fireblocks-native identifier (for example, `USDC_BASE`, `USDC_POLYGON`, `ETH_BASE`). There is no separate tokens or blockchains registry — the asset entry carries everything the facilitator needs. * Assets set `stable: true` for USD-pegged stablecoins (no network call at request time) or `price_symbol: ""` for live-priced assets. * Products use `pricing[]` to list accepted assets, and optionally a `usd_price` to convert from. Legacy `{asset_id, price}` on a product is still accepted and normalized to a single-entry `pricing[]`. API key **hashes** are persisted; plaintext is returned once at creation and never again. If you lose a key, revoke it and mint a new one. ## Auth model The facilitator exposes two independent auth surfaces. They are never mixed. | Path | Auth | Purpose | | - | - | - | | `/api/admin/*` | JWT (HS256 or JWKS) | Operators. Per-route scope check. | | `/api/payments/*` | Persistent API key | Machine clients (merchant servers, agents). Scoped to one configuration. | | `/api/discovery/*`, `/api/health`, `/api/payments/supported` | Public | — | ### Management API (JWT) Admin tokens are short-lived JWTs. In development, mint them locally from the HS256 secret scaffolded by `npm run setup`: ```bash theme={"system"} npm run setup:admin-token -- --preset full # all four admin scopes npm run setup:admin-token -- --preset readonly # admin:read + payments:read npm run setup:admin-token -- --preset payments-ops npm run setup:admin-token -- --scopes "payments:read admin:read" ``` Export as `X402_ADMIN_TOKEN` and the `x402` CLI plus any direct curl picks it up: ```bash theme={"system"} export X402_ADMIN_TOKEN= x402 payments list curl -H "authorization: Bearer $X402_ADMIN_TOKEN" http://localhost:3000/api/admin/facilitator ``` In production, point `X402_ADMIN_JWT_JWKS_URL` at your identity provider's JWKS endpoint. Your IdP issues tokens carrying `tenant_id`, `sub`, `scope` (space-delimited), and optional `configuration_ids`. ### Payment processing (API keys) Payment API tokens are opaque bearer strings minted via `x402 keys create` or `POST /api/admin/tokens`. Only the SHA-256 hash is persisted in the config file; the plaintext is shown once at creation and never again. Keys are scoped to a single configuration — a key minted for `merchant-a` cannot settle payments for `merchant-b`. Scopes you will actually use: * `process-payments` — call `/api/payments/{create,verify,settle}` * `api:read` — read-only access (reserved for future public read routes) * `*` — wildcard ## Management API The management API is the operator-facing surface. Every route is JWT-authenticated and gated by an explicit scope. ### Admin scopes | Scope | Routes | | - | - | | `admin:read` | `GET /facilitator`, `GET /assets`, `GET /products`, `GET /tokens`, `GET /fireblocks` | | `admin:write` | `POST` / `DELETE` on assets, products, tokens, and `fireblocks test` | | `payments:read` | `GET /payments`, `GET /payments/:id` | | `payments:write` | `POST /payments/:id/mark-failed`, `POST /payments/:id/refund`, `POST /payments/:id/sync`, `POST /payments/sync-all`, `POST /payments/sweep-expired` | | `*` | Wildcard — passes every admin scope check | ### Targeting a configuration When a tenant holds multiple configurations, admin requests pick one with the `X-Configuration-ID` header or `?configuration=` query parameter. If omitted, the request targets `default_configuration_id`: ```bash theme={"system"} curl -H "Authorization: Bearer $X402_ADMIN_TOKEN" \ -H "X-Configuration-ID: merchant-b" \ http://localhost:3000/api/admin/products ``` The authenticated user's `configurationIds` grant must include the chosen configuration, otherwise the request returns **403**. ### Inspecting configuration `GET /api/admin/facilitator` returns the configuration block the principal is scoped to, with `fireblocks.api_key` redacted. ### Assets `GET /api/admin/assets` and `GET /api/admin/assets/:assetId` list or fetch one configured asset. `POST /api/admin/assets` registers an asset. The facilitator calls Fireblocks' `listAssets` to fill `address` / `decimals` / `chain_id`; you supply the x402-specific fields in the body. ```bash theme={"system"} curl -X POST https://facilitator.example.com/api/admin/assets \ -H "Authorization: Bearer $X402_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "asset_id": "USDC_BASE", "transfer_mechanism": "eip-3009", "eip712_name": "USD Coin", "eip712_version": "2", "stable": true }' ``` `POST /api/admin/assets/sync` re-fetches Fireblocks-owned fields for every asset in scope. Body: `{ "apply": false }` for a dry-run diff report; `true` to write. Returns per-asset `diffs[]` and any `errors[]`. ### Products `GET /api/admin/products` and `GET /api/admin/products/:productId` list or fetch products with their asset joined. > [!NOTE] > > Adding and editing products over HTTP is not yet supported. Use the `x402 products` CLI. The HTTP endpoints above are read-only on purpose. ### API tokens The primary write endpoints for issuing machine credentials. `POST /api/admin/tokens` mints a key: ```bash theme={"system"} curl -X POST https://facilitator.example.com/api/admin/tokens \ -H "Authorization: Bearer $X402_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"scopes":["process-payments"],"label":"agent-primary"}' ``` ```json theme={"system"} { "token": "x402_ak_AbCd1234_plaintextOnlyReturnedOnce", "keyId": "ak_AbCd1234", "label": "agent-primary", "scopes": ["process-payments"] } ``` `GET /api/admin/tokens` lists keys (hashes omitted). `DELETE /api/admin/tokens/:keyId` revokes a key, returning **204** on success or **404** if the key does not exist. ### Inspecting payments `GET /api/admin/payments` and `GET /api/admin/payments/:paymentId` are read-only payment inspection. Scope: `payments:read`. Query parameters on list: `?status=`, `?limit=`, `?offset=`. ```json theme={"system"} [ { "paymentId": "pay_abc", "tenantId": "acme", "configurationId": "default", "productId": "prod_7Lm3nOp", "assetId": "USDC_BASE", "amount": 0.1, "amountBaseUnits": 100000, "recipientAddress": "0x…", "fromAddress": "0x…", "status": "completed", "transferMechanism": "eip-3009", "transactionHash": "0x…", "blockNumber": 12345678 } ] ``` ### Managing payments `POST /api/admin/payments/:paymentId/mark-failed` forces a stuck row into `failed` with an operator-supplied reason. No on-chain action — for rows where the happy-path transitions never closed out (server crash mid-settle, settlement poll timed out, and similar). Scope: `payments:write`. `POST /api/admin/payments/:paymentId/refund` refunds a payment whose on-chain leg landed (`status` is `completed` or `settled`). Submits a `CONTRACT_CALL` to the token's `transfer(to, amount)` function via Fireblocks. Status transitions: `refunding → refunded`, or `refund_failed` with the Fireblocks error recorded. Returns **409** when the payment is not refundable, **502** when the on-chain refund fails. Scope: `payments:write`. `POST /api/admin/payments/:paymentId/sync` reconciles one payment against Fireblocks. Reads the persisted `fireblocksTxId`, asks Fireblocks for the transaction's current state, and transitions the row: * `completed` with `transactionHash` and `blockNumber` on Fireblocks `COMPLETED`. * `failed` on Fireblocks `FAILED`, `CANCELLED`, `BLOCKED`, or `REJECTED`. * No-op while the transaction is still in-flight. `POST /api/admin/payments/sync-all` runs the same reconcile pass across every `settling` row in the caller's scope. The same pass fires at boot for every configuration (processing-role only); opt out with `X402_RECONCILE_ON_BOOT=false` when running many processing instances against a shared store. `POST /api/admin/payments/sweep-expired` bulk-expires every pending payment whose `expiresAt` is in the past. Idempotent per row — safe to run on a schedule. ### Payment statuses Payments transition through: `pending → verified → settling → completed`, plus the terminal states `settled`, `refunding`, `refunded`, `refund_failed`, `expired`, and `failed`. > [!NOTE] > > **Reconciler-ownership invariant.** Once a `fireblocksTxId` is attached to a row, the settle route never preempts it into `failed` on transient errors. The underlying transaction may still land, so the row stays `settling` and the reconciler decides terminality from Fireblocks's own truth. Rows without a `fireblocksTxId` still follow the normal settle path. ## CLI reference The facilitator ships two CLI surfaces with a clean split: 1. `x402` — a remote HTTP client for `/api/admin/*`. Pure API client, safe to run from anywhere with `X402_ADMIN_TOKEN` set. Targets `X402_URL` (default `http://localhost:3000`). 2. `npm run setup*` — local bootstrap scripts. Touch the filesystem (scaffold config, mint JWTs from the on-disk secret, import a legacy SQLite database). Must run on the same host as the server. ### x402 remote CLI Install once per developer: ```bash theme={"system"} npm run build && npm link # puts `x402` on your PATH ``` Every command that operates on a configuration takes `-c, --configuration ` (or env `CONFIGURATION=`); omitted means the server default. ```bash theme={"system"} # Config (read-only) x402 config show x402 config validate x402 config configurations # Fireblocks (read-only + operational test) x402 fireblocks show x402 fireblocks test [--chain-id 8453] [--create-missing] # API keys x402 keys list [--json] x402 keys create --scopes process-payments [--label agent] x402 keys revoke # Assets x402 assets list [--json] x402 assets show x402 assets import \ --transfer-mechanism eip-3009 \ --eip712-name "" --eip712-version \ [--stable] [--price-symbol ] [--force] x402 assets sync [--apply] x402 assets remove # Products x402 products list [--json] x402 products show x402 products add --name Premium --endpoint /premium --asset USDC_BASE --usd-price 0.10 x402 products add --name Premium --endpoint /premium --asset USDC_BASE --price 100000 x402 products remove # Payments — read x402 payments list [--status failed] [--limit 20] [--json] x402 payments get # Payments — state changes x402 payments mark-failed --reason "..." x402 payments refund x402 payments sync x402 payments sync-all x402 payments sweep-expired ``` Every command accepts `--url ` and `--token ` to override the env defaults. ### Local setup scripts These run through `npm run`. They never hit HTTP. ```bash theme={"system"} npm run setup # scaffold config/facilitator.json + secrets/jwt-hs256.key npm run setup -- --force # rotate both (destructive) npm run setup:admin-token -- --preset full npm run setup:admin-token -- --preset readonly npm run setup:admin-token -- --preset payments-ops npm run setup:admin-token -- --preset wildcard npm run setup:admin-token -- --scopes "payments:read admin:read" --ttl 30m npm run setup:migrate -- --db ./data/legacy.db [--force] ``` All config edits go through atomic tmpfile-plus-rename, so it is safe to run `x402 products add` while the server is running (nodemon reloads). ### End-to-end test harness ```bash theme={"system"} npm run e2e ``` Assumes the facilitator, the example merchant, and the EOA are already set up. Fires each configured transfer mechanism (`eip-3009`, `permit2`, `erc7710`) in sequence against the test client, polls the admin API until each payment row reaches `completed`, and prints a pass/fail summary with block explorer links. Exits `0` if every mechanism passed. Idempotent — safe to re-run. ## Payment Instruction Integrity Optional. When enabled on a configuration, the facilitator signs every `/api/payments/create` response with an ES256 keypair and attaches the envelope two ways: 1. As an `integrity` top-level field on the JSON body. 2. As an `X-402-Integrity` response header (mirrored by `@x402/express`). Wallets that implement the [PII extension](https://x402.org) fetch the `did:web` document referenced by the envelope, verify the signature, and refuse to sign any payment whose body was altered between the facilitator and the wallet. ### What the facilitator signs The envelope covers a payment-critical slice of the 402 body — specifically `{x402Version, accepts}` — plus the envelope's own `iat` and `exp`. `resource.url`, `error`, and `extensions` are intentionally excluded because the merchant SDK rewrites `resource.url` to its own public origin before emitting the 402; signing it would invalidate every response. The payment data the wallet actually commits to (amount, asset, payTo, network, scheme — all in `accepts[]`) is signed. Canonical bytes: ``` SHA-256( JCS({x402Version, accepts}) || "\n" || iat || "\n" || exp ) ``` JCS is RFC 8785 JSON Canonicalization. ### Envelope shape Base64url-encoded JSON: ```json theme={"system"} { "v": 1, "did": "did:web:api.example.com", "kid": "key-1", "alg": "ES256", "iat": 1776521334, "exp": 1776521634, "sig": "" } ``` ### Enabling it `npm run setup` scaffolds `./secrets/integrity-p256.pem` (a P-256 keypair) for you. Flip integrity on per configuration: ```jsonc theme={"system"} { "configuration_id": "default", "public_host": "http://localhost:3000", "integrity": { "enabled": true, "private_key_path": "./secrets/integrity-p256.pem", "did": "did:web:localhost%3A3000", "kid": "key-1", "alg": "ES256", "ttl_seconds": 300, "serve_did_document": true } } ``` When `integrity.serve_did_document: true`, the facilitator serves the DID document at `GET /.well-known/did.json` (resolved by `Host` header against `public_host`). If you would rather host the `did.json` yourself, leave `serve_did_document: false` and publish the public key at whatever URL `did:web:` resolves to. ### Wallet-side verification The bundled test client has a `VERIFY_INTEGRITY=true` flag that decodes the envelope, checks `iat` and `exp`, resolves `did:web:`, verifies the ES256 signature, and aborts the flow if verification fails. ```bash theme={"system"} VERIFY_INTEGRITY=true MECHANISM=eip3009 CHAIN=11155111 npm run dev ``` Add `REQUIRE_INTEGRITY=true` to also reject quotes that do not carry an envelope at all. ### Scope and limitations What integrity signs: `{x402Version, accepts, iat, exp}`. What it does not sign: `resource.url`, `error`, `extensions` — these are mutable by the merchant SDK. Supported algorithm: `ES256` (P-256). Not yet supported: `ES256K`, `EdDSA`, `did:webvh`, multiple keys per DID, on-chain registry binding. The PII specification is a [draft](https://x402.org); the envelope format follows it, but the multi-accept canonical form is a documented extension. ## Running in production ### Environment variables All are optional unless noted. | Variable | Default | Notes | | - | - | - | | `PORT` | `3000` | Server listen port | | `CONFIG_PATH` | `./config/facilitator.json` | Where to load config | | `PAYMENT_STORE` | `sqlite` | `memory`, `sqlite`, or `postgres` | | `DB_PATH` | `./data/facilitator.db` | sqlite only | | `POSTGRES_URL` | — | Required when `PAYMENT_STORE=postgres` | | `ALLOWED_ORIGINS` | `http://localhost:$PORT` | Comma-separated CORS allowlist | | `X402_ADMIN_JWT_SECRET` | — | Inline HS256 secret; takes precedence over the file-based default | | `X402_ADMIN_JWT_SECRET_FILE` | `./secrets/jwt-hs256.key` | File-based HS256 secret; scaffolded by `npm run setup` | | `X402_ADMIN_JWT_JWKS_URL` | — | JWKS endpoint for production (RS256 or ES256); overrides HS256 when set | | `X402_ADMIN_JWT_ISSUER` | — | Optional `iss` claim to require on every admin JWT | | `X402_ADMIN_JWT_AUDIENCE` | — | Optional `aud` claim to require on every admin JWT | | `X402_URL` | `http://localhost:3000` | Read by the `x402` CLI | | `X402_ADMIN_TOKEN` | — | Bearer JWT for the `x402` CLI | | `X402_ALLOW_MAINNET` | — | Must be `true` to register or run with mainnet assets. Default-deny. | | `X402_RECONCILE_ON_BOOT` | `true` | Set to `false` when running many processing instances against a shared store. | | `COINGECKO_API_KEY` | — | Optional; enables CoinGecko Pro. Free tier works unauthenticated. | | `NODE_ENV` | `development` | Standard Node environment | Fireblocks credentials do not live in environment variables — they belong in `config/facilitator.json`. ### Network policy The facilitator tracks every asset's network via an `is_testnet` field (populated at import time from Fireblocks' `blockchain.onchain.test`). By default, mainnet is denied: * **Boot** — the server refuses to start if any configured asset has `is_testnet: false` and `X402_ALLOW_MAINNET` is not set to `true`. The error lists the offending `(asset_id, chain_id)` pairs. * **Import** — `POST /api/admin/assets` (and `x402 assets import`) returns **403** when the hydrated asset is mainnet and the flag is off. To opt in: ```bash theme={"system"} X402_ALLOW_MAINNET=true npm start ``` The server banner prints `network policy: mainnet OK (X402_ALLOW_MAINNET=true)` or `testnet-only (default)` at startup, so there is no ambiguity. ### Build and run compiled ```bash theme={"system"} npm run build NODE_ENV=production \ X402_ROLE=processing \ X402_ADMIN_JWT_JWKS_URL=https://auth.yourco.example.com/.well-known/jwks.json \ PAYMENT_STORE=postgres \ POSTGRES_URL=postgres://:@host:5432/x402 \ npm start ``` For management in production, point `X402_ADMIN_JWT_JWKS_URL` at your JWT issuer's public keys. HS256 works but asymmetric or JWKS is the right choice at scale. Without either, the admin API rejects everything. ### Role split The same binary runs in three profiles, selected by `X402_ROLE`: * `processing` — handles `/api/payments/*` and the reconciler. Run multiple of these behind a load balancer. * `management` — handles `/api/admin/*`. Smaller surface, separate scaling. * `all` (default) — both. Right for single-instance and development deployments. When you run many `processing` instances against a shared payment store, set `X402_RECONCILE_ON_BOOT=false` on all but one so only one instance leads the boot-time reconcile pass. ## Multiple merchants on one facilitator Payment-service providers who operate the facilitator on behalf of many merchants use one configuration per merchant: ```jsonc theme={"system"} { "tenant_id": "acme-payments-provider", "default_configuration_id": "merchant-a", "configurations": [ { "configuration_id": "merchant-a", "public_host": "https://pay.merchant-a.com", "fireblocks": { /* merchant A's vault */ }, "products": [ /* … */ ], "api_keys": [ /* … */ ] }, { "configuration_id": "merchant-b", "public_host": "https://pay.merchant-b.com", "fireblocks": { /* merchant B's vault */ }, "products": [ /* … */ ], "api_keys": [ /* … */ ] } ] } ``` Requests are routed to the right configuration by matching the request's `Host` header against each configuration's `public_host`. API keys are bound to the configuration that issued them — a key from `merchant-a` cannot settle payments for `merchant-b`. For a single-merchant deployment, leave the default single-configuration layout in place. ## Operator responsibilities and risk Running an x402 facilitator means initiating real on-chain value transfers from a Fireblocks vault you control. Before deploying, read the full operator-facing disclaimer: * [`DISCLAIMER.md`](https://github.com/fireblocks/x402-facilitator/blob/main/DISCLAIMER.md) — covers irreversibility of on-chain transfers, the absence of a third-party security audit at publication, third-party contract risk (Permit2, the MetaMask Delegation Framework, ERC-20 tokens including USDC and USDT blacklisting and pause functions), the non-advisory nature of the code, jurisdictional and data-protection considerations (including GDPR and UK GDPR), the Fireblocks trademark policy, the absence of any fiduciary or custodial relationship, and the independence of the x402 protocol specification. > [!WARNING] > > On-chain transfers initiated by the facilitator are irreversible. Bugs in your integration, misconfigured pricing or destination addresses, or unexpected upstream contract behavior may result in permanent loss of funds. Test thoroughly on testnets before enabling mainnet, and review every configuration change. To report a suspected security issue in the facilitator, email [security@fireblocks.com](mailto:security@fireblocks.com) directly — do not file a public issue. ## License The facilitator is licensed under Apache License 2.0. See [`LICENSE`](https://github.com/fireblocks/x402-facilitator/blob/main/LICENSE) for the full legal text. The license does not grant rights to use the Fireblocks name, logos, or other trademarks — see the trademark clause in [`DISCLAIMER.md`](https://github.com/fireblocks/x402-facilitator/blob/main/DISCLAIMER.md). # Overview Source: https://developers.fireblocks.com/docs/x402-facilitator-overview Charge for any HTTP endpoint with a one-shot cryptographic signature. Verify and settle x402 payments on-chain through Fireblocks. The Fireblocks x402 Facilitator turns HTTP 402 ("Payment Required") into a working payment rail. A client signs an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) message authorizing a specific transfer; your merchant server calls the facilitator to verify the signature and settle the on-chain transfer through Fireblocks. Customer traffic never passes through the facilitator — your server keeps serving its own requests. The facilitator is open-source under Apache License 2.0. Clone it from [github.com/fireblocks/x402-facilitator](https://github.com/fireblocks/x402-facilitator) to deploy. ## What x402 is [x402](https://x402.org) is an open protocol that gives the long-reserved HTTP 402 status code a job. A server responds **402** with a price quote; the client signs an EIP-712 message authorizing exactly that payment; the client retries the same request with a `payment-signature` header; the server verifies and delivers the resource. The protocol is designed for machine-to-machine and agent-to-service commerce: API monetization, pay-per-call data, agentic checkout, content metering. Payments are denominated in ERC-20 tokens (commonly USDC) on EVM-compatible networks. The signature authorizes a single transfer of a specific amount — there is no long-lived approval, no subscription state, and no payment account to maintain. ### Where Fireblocks fits In the x402 model, a **facilitator** is the service merchants call to verify signatures and settle payments. Fireblocks provides the facilitator implementation and the on-chain settlement layer: * The facilitator validates the client's EIP-712 signature off-chain. * When the merchant requests settlement, the facilitator submits a `CONTRACT_CALL` transaction through Fireblocks, drawing from the merchant's vault account. * Raw private keys never leave Fireblocks. The facilitator process holds no signing material. The merchant runs their own server and middleware, returns the 402 themselves, and calls the facilitator at two well-defined decision points. The facilitator is API-only — it does not proxy customer traffic. ## What's included Out of the box, the facilitator gives you: * **API-only surface** — `/api/payments/{create,verify,settle}`, `/api/discovery/*`, and a management API. No reverse proxy of merchant traffic. * **Fireblocks settlement** — on-chain transfers via `CONTRACT_CALL`. No raw private keys in the facilitator process. * **Pluggable payment store** — in-memory (tests), SQLite (default), or PostgreSQL. * **Four transfer mechanisms** — `eip-3009` (USDC-style), `permit2` and `upto-permit2` (any ERC-20), `erc7710` (smart-account delegation). * **Multi-configuration** — one deployment can host many merchants, each with their own Fireblocks vault, products, and API keys. * **Two auth surfaces** — persistent API keys (opaque) for payment processing; JWT (HS256 or JWKS) for the management API. Never mixed. * **Role-based profile** — run the same binary as `processing`, `management`, or `all`. * **Remote admin CLI** (`x402`) — pure HTTP client for `/api/admin/*`. Authenticates with a JWT minted locally. Works from a laptop, CI, or a dashboard. * **Local bootstrap** (`npm run setup`) — scaffold config, rotate JWT secrets, import legacy SQLite, all filesystem-only. * **Testnet-only by default** — the facilitator refuses to boot or register mainnet assets unless `X402_ALLOW_MAINNET=true`. ## How a payment flows ```mermaid theme={"system"} sequenceDiagram participant C as Client participant M as Merchant server participant F as Facilitator participant FB as Fireblocks C->>M: GET /premium M-->>C: 402 Payment Required
{ amount, asset, payTo } Note over C: Sign EIP-712 authorization C->>M: GET /premium
+ payment-signature M->>F: POST /api/payments/verify F-->>F: Validate signature off-chain F-->>M: { isValid: true, payer } M->>F: POST /api/payments/settle F->>FB: CONTRACT_CALL (token transfer) FB-->>F: Transaction confirmed F-->>M: { success, txHash } M-->>C: 200 OK + PAYMENT-RESPONSE ``` 1. The client sends a normal HTTP request to your server. 2. Your middleware sees no `payment-signature` header and returns **402** with a JSON quote describing the amount, asset, and recipient. 3. The client signs an EIP-712 message authorizing exactly that transfer. No gas, no on-chain transaction at this stage. 4. The client retries the same request with `payment-signature: `. 5. Your middleware calls `POST /api/payments/verify` on the facilitator. If the signature is valid, it calls `POST /api/payments/settle`, then returns the resource with a `PAYMENT-RESPONSE` header. ## Prerequisites * Node.js 20 or later. * A Fireblocks workspace with an API key, a vault account ID, and the PEM-format API secret file. If you are evaluating the facilitator locally and do not have Fireblocks yet, the server still starts with an empty config — you just cannot call `fireblocks test` or settle payments. ## Quick start The walkthrough below takes you from a fresh clone to a running facilitator with one merchant configured. ```bash theme={"system"} git clone https://github.com/fireblocks/x402-facilitator cd x402-facilitator npm install # 1. Local bootstrap — scaffolds config/facilitator.json + secrets/jwt-hs256.key npm run setup # 2. Fill in Fireblocks credentials: edit config/facilitator.json # fireblocks.api_key → your Fireblocks API key # fireblocks.api_secret_path → path to your PEM (e.g. ./secrets/fireblocks.pem) # 3. Start the facilitator npm run dev # facilitator on :3000 # 4. Build and install the remote CLI globally (one-time) npm run build && npm link # puts `x402` on your PATH # 5. Mint a local admin JWT and export it npm run setup:admin-token -- --preset full --ttl 2h export X402_ADMIN_TOKEN= # 6. Activate Fireblocks vault wallets and cache the receiver addresses x402 fireblocks test --create-missing # 7. Import an asset from Fireblocks x402 assets import USDC_BASECHAIN_ETH_TEST5_8SH8 \ --transfer-mechanism eip-3009 \ --eip712-name "USDC" --eip712-version 2 \ --stable # 8. Declare a product (USD-priced) x402 products add --name "Premium" --endpoint /premium \ --usd-price 0.01 --asset USDC_BASECHAIN_ETH_TEST5_8SH8 # 9. Mint a machine API key for the merchant server x402 keys create --scopes process-payments --label my-merchant # → prints the key once; copy it into your merchant app's config ``` Verify the server is up: ```bash theme={"system"} curl http://localhost:3000/api/health # {"status":"ok","role":"all","mechanisms":[...], …} ``` > [!WARNING] > > Never commit `config/facilitator.json` with real Fireblocks credentials, and never commit the PEM secret. Both belong in a secrets manager (for example, AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault) in any non-development deployment. The repository's `.gitignore` excludes both paths by default — confirm this remains true in your fork. > [!WARNING] > > Treat the merchant API key as a secret. The plaintext is returned **once** at creation; only a SHA-256 hash is persisted. Store it in a secrets manager and inject it into your merchant application at runtime — never hard-code it, never commit it, and never expose it through a client-side surface. If you lose a key, revoke it and mint a new one. ## What to read next * [Integration](/docs/x402-facilitator-integration) — wire the facilitator into your merchant server, the payment processing API endpoints, transfer mechanisms, and pricing modes. * [Operating and production](/docs/x402-facilitator-operations) — the config file, auth model, management API, CLI reference, Payment Instruction Integrity, production deployment, and operator responsibilities. ## Helpful Links * [Repository](https://github.com/fireblocks/x402-facilitator) — source code, issue tracker, releases. * [`ARCHITECTURE.md`](https://github.com/fireblocks/x402-facilitator/blob/main/ARCHITECTURE.md) — internal source layout, repository conventions, and contributor-facing detail. * [`DISCLAIMER.md`](https://github.com/fireblocks/x402-facilitator/blob/main/DISCLAIMER.md) — operator-facing risk and compliance notices. * [x402.org](https://x402.org) — the open protocol specification. # Build an Agentic Product Catalog Source: https://developers.fireblocks.com/docs/x402-facilitator-product-catalog Declare payment-gated products on the Fireblocks x402 Facilitator and publish them so AI agents can discover, price, and pay for your services automatically. A product catalog is how you describe what you sell to autonomous agents. On the Fireblocks x402 Facilitator, each product is a payment-gated HTTP endpoint with a price, an accepted-asset list, and metadata. Your own agents and customers reach products directly through the `402` flow on your server. The broader agent ecosystem finds them through the public discovery API, which follows the same shape as the [Coinbase x402 Bazaar](https://docs.cdp.coinbase.com/x402/bazaar) catalog. This page covers declaring products, pricing them across assets, and deciding which ones to publish. With x402 you monetize an endpoint and let machines buy access without an account, a subscription, or a human in the loop. To fund an agent and control what it spends, see [Fund and Control Agent Spending](/docs/x402-agent-spending-controls). ## What a product is A product binds a URL path on your server to a price and a set of accepted assets. The facilitator stores products per configuration; each product carries these fields: | Field | Purpose | | - | - | | `product_id` | Server-generated identifier (`prod_…`). | | `name` | Human-readable label shown to operators. | | `endpoint` | The URL path you gate, starting with `/` (for example, `/premium`). | | `scheme` | x402 payment scheme: `exact` (fixed price) or `upto` (metered ceiling). | | `usd_price` | USD price in fractional dollars. Required whenever any pricing row converts from USD. | | `pricing[]` | One row per accepted asset. Each row is a fixed native amount or a USD-converted amount. | | `description` | Free-text shown in the discovery catalog. Falls back to `name` when absent. | | `mime_type` | Response content type advertised to agents. Defaults to `application/json`. | | `category` | Optional grouping used as a discovery filter (for example, `data`, `inference`). | | `is_discoverable` | Whether the product appears in the public discovery API. Defaults to `false`. | A product is the unit an agent reasons about: a single endpoint, a price it can compare, and an asset it already holds. ## Declare a product Products are managed with the `x402` CLI. (Creating and editing products over HTTP is intentionally read-only; the management API exposes products for inspection only.) The simplest product gates one endpoint at a fixed USD price, settled in USDC: ```bash theme={"system"} x402 products add \ --name "Premium Data" \ --endpoint /premium \ --usd-price 0.01 \ --asset USDC_BASECHAIN_ETH_TEST5_8SH8 ``` To price directly in an asset's base units instead of converting from USD, pass the amount with the asset or use `--price`: ```bash theme={"system"} # 0.10 USDC expressed in base units (6 decimals) x402 products add --name "Premium" --endpoint /premium \ --asset USDC_BASECHAIN_ETH_TEST5_8SH8:100000 # Equivalent legacy shorthand for a single-asset native amount x402 products add --name "Premium" --endpoint /premium \ --asset USDC_BASECHAIN_ETH_TEST5_8SH8 --price 100000 ``` Each accepted asset must already be imported into the facilitator's asset catalog (`x402 assets import`) before you can reference it in a product. Endpoints are unique within a configuration: adding a second product on a path that already exists returns `409`. ### Inspect the catalog ```bash theme={"system"} x402 products list # table of products in the configuration x402 products show # full record, with each pricing row's asset joined x402 products remove # delete a product ``` ## Price across multiple assets Each product carries a `pricing[]` table with one row per accepted asset. A row is either **native-denomination** (an explicit `amount` in the asset's base units) or **USD-converted** (`amount` is null, and the product's `usd_price` is converted at request time). You can mix both on the same product. Repeat `--asset` to accept several assets at once: ```bash theme={"system"} x402 products add --name "Premium" --endpoint /premium --usd-price 0.10 \ --asset USDC_BASE \ --asset USDC_POLYGON:90000 \ --asset ETH_BASE ``` This advertises three ways to pay for the same resource: convert \$0.10 to USDC on Base, charge a fixed 0.09 USDC on Polygon, and convert \$0.10 to ETH at the current rate. The facilitator emits one `accepts[]` entry per pricing row in the `402` response, and the agent picks whichever asset it holds. For how USD conversion resolves and how the same asset can be offered under several transfer mechanisms, see [Pricing modes](/docs/x402-facilitator-integration#pricing-modes). > [!TIP] > > Offer at least one stablecoin priced row. Agents compare quotes across services, and a stable, low-gas option (for example, USDC on Base) is the path most buyers can settle immediately. ## Publish to the discovery catalog By default a product is private: agents reach it only by hitting your endpoint and receiving a `402`. To list a product in the public discovery API, mark it discoverable and give it a category: ```bash theme={"system"} x402 products add --name "Premium Data" --endpoint /premium \ --usd-price 0.01 --asset USDC_BASE \ --description "Real-time market data feed" \ --category data \ --discoverable ``` The facilitator exposes the catalog at `GET /api/discovery/resources`. This endpoint is **public** (no authentication) and is scoped to a configuration by the request's `Host` header, so each merchant's public host serves only its own discoverable products. ```bash theme={"system"} curl "https://pay.myservice.com/api/discovery/resources?category=data&limit=20" ``` The response lists each discoverable product as a resource an agent can act on: ```json theme={"system"} { "x402Version": 2, "items": [ { "resource": "https://pay.myservice.com/premium", "type": "http", "x402Version": 2, "accepts": [ { "scheme": "exact", "network": "eip155:8453", "amount": "10000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" } ], "lastUpdated": 1776521334, "metadata": { "category": "data", "description": "Real-time market data feed", "mimeType": "application/json" } } ], "pagination": { "limit": 20, "offset": 0, "total": 1 } } ``` Query parameters: `category` filters by product category, `type` accepts `http` (the only supported resource type today), and `limit` (max 100) plus `offset` paginate. > [!NOTE] > > **Only fixed-price rows appear in discovery.** The catalog is a static index, so a discoverable product surfaces one `accepts[]` entry per **native-denomination** pricing row. USD-converted rows (where `amount` is null) are skipped, because their price depends on a live exchange rate. A product whose rows are all USD-converted still works through the live `402` flow, but it will not appear in the catalog. To list in discovery, give the product at least one native-denomination row. ### How discovery fits the wider ecosystem The discovery response shape matches the Coinbase x402 Bazaar catalog format, so agents and directories built for the Bazaar can read your facilitator's catalog directly. Discovery and settlement stay separate: the discovery API only advertises what you sell, while payment still flows through your `402` endpoint and the `/api/payments/*` API. Publishing a product does not expose your Fireblocks vault, your API keys, or your customer traffic. ## Build a catalog for your customers and for the open market The `is_discoverable` flag lets one facilitator serve two audiences from the same product set: * **Your own agents and customers.** Leave products private (`is_discoverable: false`). They are reachable only by callers who already know your endpoint, which suits internal tools, partner integrations, and paid APIs you market yourself. The `402` flow and the [payment processing API](/docs/x402-facilitator-integration#the-payment-processing-api) work identically whether or not a product is discoverable. * **The open agent ecosystem.** Mark products discoverable so any agent browsing the catalog can find, price, and pay for them without prior knowledge of your service. A common pattern is a tiered catalog: a small set of discoverable products as a public on-ramp, with richer or higher-value endpoints kept private for authenticated partners. ### Host many catalogs on one facilitator A single facilitator process can host a separate catalog per merchant using one configuration per merchant. Each configuration has its own `public_host`, Fireblocks vault, API keys, and product list, and discovery requests are routed to the right catalog by `Host` header. This is the model a payment-service provider uses to run catalogs on behalf of many sellers. See [Multiple merchants on one facilitator](/docs/x402-facilitator-operations#multiple-merchants-on-one-facilitator). ## How an agent buys from your catalog Once a product is published, the purchase path is fully machine-driven: ```mermaid theme={"system"} sequenceDiagram participant A as Buyer agent participant D as Discovery API participant M as Your server participant F as Facilitator A->>D: GET /api/discovery/resources?category=data D-->>A: catalog (resource URLs, prices, assets) A->>M: GET /premium M-->>A: 402 Payment Required (quote) Note over A: Sign EIP-712 authorization A->>M: GET /premium + payment-signature M->>F: verify + settle F-->>M: settled on-chain M-->>A: 200 OK + resource ``` 1. The agent browses the discovery catalog and selects a resource whose price and asset suit it. 2. The agent requests the resource and receives a `402` quote (or fetches it ahead of time with [`POST /api/payments/create`](/docs/x402-facilitator-integration#post-/api/payments/create)). 3. The agent signs an EIP-712 authorization and retries with a `payment-signature` header. 4. Your middleware verifies and settles through the facilitator, then serves the resource. Whether you settle before or after serving is the [settlement mode](/docs/x402-facilitator-integration#when-to-settle) choice. You never write agent-facing payment code beyond the middleware that returns the `402` and calls verify/settle. The catalog does the rest. ## What to read next * [Integration](/docs/x402-facilitator-integration) — the middleware pattern, the payment processing API, transfer mechanisms, and pricing modes. * [Fund and Control Agent Spending](/docs/x402-agent-spending-controls) — funding an agent from a Fireblocks vault and controlling what it spends. * [Operating and production](/docs/x402-facilitator-operations) — config file, auth model, management API, CLI reference, and multi-merchant hosting. # Get Started with x402 on Fireblocks Source: https://developers.fireblocks.com/docs/x402-getting-started New to x402? Understand what it is, and decide whether you're selling, buying, or both. x402 lets software pay for an HTTP request the moment it makes it, with no account, subscription, or card on file. A server answers a request with `402 Payment Required` and a price; the caller signs a one-time authorization to pay exactly that amount in a stablecoin such as USDC; the server verifies and delivers. It is built for machine-to-machine and agent-to-service commerce: API monetization, pay-per-call data, agentic checkout, and content metering. This page orients you and points you to the right starting path. For the protocol mechanics and the terms used throughout these docs, see [x402 Concepts and Glossary](/docs/x402-concepts). Fireblocks gives you both halves of an x402 deployment, with private keys held in Fireblocks rather than in your application: * A **facilitator** that verifies signatures and settles payments on-chain. * An **agent** that pays for x402-protected resources from a Fireblocks vault. ## Are you selling, buying, or both? Your first decision is which side of an x402 payment you are on. Most teams start with one and add the other later. | You want to… | You are the… | Start here | | - | - | - | | Charge for an API, dataset, or endpoint | Seller (resource server + facilitator) | [Facilitator Overview](/docs/x402-facilitator-overview) | | Let an AI agent pay for resources autonomously | Buyer (agent) | [x402 Agent](/docs/x402-agent) | | Run a marketplace where agents pay your services | Both | [Facilitator Overview](/docs/x402-facilitator-overview) and [x402 Agent](/docs/x402-agent) | ### Selling: run a facilitator The facilitator is the service your merchant server calls to verify a payment signature and settle the transfer on-chain through your Fireblocks vault. Your server keeps serving its own traffic and returns the `402` itself; customer traffic never passes through the facilitator. Once you are running, you declare the things you sell as products and, optionally, publish them so agents can discover them. * [Facilitator Overview](/docs/x402-facilitator-overview) — what the facilitator does and a clone-to-running quick start. * [Integration](/docs/x402-facilitator-integration) — wire the facilitator into your merchant server. * [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog) — declare, price, and publish what you sell. * [Operating and Production](/docs/x402-facilitator-operations) — config, auth, the management API, and production deployment. ### Buying: run an agent The x402 Agent gives any autonomous agent the ability to pay for `402`-protected resources using a Fireblocks vault. It detects the `402`, signs the payment authorization through Fireblocks, and retries the request. It ships as an MCP server (for Claude Desktop, Cursor, and other MCP clients) and a CLI. * [x402 Agent](/docs/x402-agent) — the agent library, its MCP server and CLI, and secure deployment. * [Fund and Control Agent Spending](/docs/x402-agent-spending-controls) — fund agents and bound what they spend at company scale. ## Your path to a first payment If you are evaluating x402 hands-on, this is the fastest route from nothing to a settled payment on a testnet: 1. **Learn the vocabulary.** Skim [x402 Concepts and Glossary](/docs/x402-concepts) so the rest of the docs read clearly. 2. **Set up prerequisites.** Create a Fireblocks workspace, an API user, and a vault, and get testnet USDC and gas. See [Prerequisites and Testnet Funds](/docs/x402-prerequisites). 3. **Run the quickstart.** Wire the example merchant, facilitator, and test client into one settled payment. See [x402 Quickstart](/docs/x402-quickstart). 4. **Go deeper** on the side you care about: [Integration](/docs/x402-facilitator-integration) for selling, or [Fund and Control Agent Spending](/docs/x402-agent-spending-controls) for buying. > [!TIP] > > x402 is testnet-only by default on the Fireblocks facilitator. It refuses to register mainnet assets or boot with them unless you explicitly opt in. Evaluate on a testnet first, then read [Operating and Production](/docs/x402-facilitator-operations#network-policy) before enabling mainnet. ## What to read next * [x402 Concepts and Glossary](/docs/x402-concepts) — every term used across these docs. * [Prerequisites and Testnet Funds](/docs/x402-prerequisites) — what you need before you start, and how to get testnet money. * [x402 Quickstart](/docs/x402-quickstart) — your first settled payment, end to end. * [Supported Networks and Assets](/docs/x402-networks-assets) — which chains and tokens work, and on which networks. # Supported Networks and Assets Source: https://developers.fireblocks.com/docs/x402-networks-assets Which blockchains and tokens x402 on Fireblocks supports, how assets are identified, testnet vs mainnet policy, and how the network you accept affects cost. x402 payments settle on EVM-compatible blockchains in ERC-20 tokens, most commonly USDC. This page covers which networks and assets work, how they are identified, and how the testnet-only default protects you until you are ready for real value. For the terms used here, see [x402 Concepts and Glossary](/docs/x402-concepts). ## How networks and assets are identified A network is an EVM chain, written in CAIP-2 form as `eip155:` in payment requirements (for example, `eip155:8453` for Base). An asset is a token on one of those chains, identified to the facilitator by a Fireblocks-native asset ID such as `USDC_BASE` or `USDC_BASECHAIN_ETH_TEST5_8SH8`. The asset entry carries everything the facilitator needs: contract address, decimals, chain ID, and the EIP-712 domain fields used to verify signatures. The facilitator does not ship a fixed network list. It accepts any EVM asset you import from your Fireblocks workspace with `x402 assets import`, and fills the contract address, decimals, and chain ID from Fireblocks at import time. What you can settle is therefore what your Fireblocks workspace supports, subject to the testnet policy below. ## Networks the agent signs on The [x402 Agent](/docs/x402-agent) maps chain IDs to Fireblocks asset IDs for signing, which fixes the set of networks it pays on: | Network | Chain ID | Fireblocks base asset | Type | | - | - | - | - | | Base | 8453 | `BASECHAIN_ETH` | Mainnet | | Base Sepolia | 84532 | `BASECHAIN_ETH_TEST5` | Testnet | | Ethereum | 1 | `ETH` | Mainnet | | Ethereum Sepolia | 11155111 | `ETH_TEST5` | Testnet | | Arbitrum | 42161 | `ETH-AETH` | Mainnet | | Optimism | 10 | `ETH-OPT` | Mainnet | | Polygon | 137 | `MATIC_POLYGON` | Mainnet | Base and Base Sepolia are the common choice for x402 because settlement is fast and gas is a fraction of a cent. ## Assets and pricing The common payment asset is **USDC**, a USD-pegged stablecoin that implements EIP-3009 and so works with the default `eip-3009` transfer mechanism. The facilitator also settles other ERC-20s through the Permit2 mechanisms. To be quotable, each imported asset needs one of two pricing signals: * **`stable: true`** for USD-pegged stablecoins, which the facilitator prices at 1:1 with USD without a network call. * **`price_symbol: ""`** for live-priced assets (for example, `ethereum`, `weth`, `matic-network`), which the facilitator converts at request time. An asset with neither signal cannot be priced in USD and is dropped from a `402` response rather than rejected; other accepted assets stay available. For how products convert from USD and mix assets, see [Pricing modes](/docs/x402-facilitator-integration#pricing-modes). > [!NOTE] > > Stablecoins such as USDC and USDT include issuer-controlled blacklist and pause functions. A frozen or paused token can block a transfer outside your control. Review the third-party contract risks in the facilitator's [`DISCLAIMER.md`](https://github.com/fireblocks/x402-facilitator/blob/main/DISCLAIMER.md) before accepting an asset on mainnet. ## Testnet-only by default The Fireblocks facilitator refuses to handle mainnet value unless you explicitly opt in. Each asset records an `is_testnet` flag, populated from Fireblocks at import: * **At boot**, the server refuses to start if any configured asset is mainnet and `X402_ALLOW_MAINNET` is not `true`, listing the offending assets. * **At import**, `x402 assets import` (and the admin API) returns `403` for a mainnet asset while the flag is off. To opt in, set the flag explicitly: ```bash theme={"system"} X402_ALLOW_MAINNET=true npm start ``` The startup banner states which policy is active, so there is no ambiguity. See [Network policy](/docs/x402-facilitator-operations#network-policy). ## Cost and the network you accept Settlement gas is paid by the facilitator's vault and varies sharply by network: cents or less on Base and other L2s, potentially more than a dollar on Ethereum mainnet. For small payments this matters. A product quoted at \$0.01 should not accept a mainnet chain whose settlement gas exceeds the payment, or the merchant loses money on every sale. The facilitator includes a gas-cost estimation interface for chain-aware pricing; see [Pricing modes](/docs/x402-facilitator-integration#pricing-modes). ## What to read next * [Prerequisites and Testnet Funds](/docs/x402-prerequisites) — get testnet USDC and gas for the networks above. * [Pricing modes](/docs/x402-facilitator-integration#pricing-modes) — price products across assets and convert from USD. * [Operating and Production](/docs/x402-facilitator-operations#network-policy) — the full mainnet opt-in and production deployment. # Prerequisites and Testnet Funds Source: https://developers.fireblocks.com/docs/x402-prerequisites What you need before running x402 on Fireblocks: Node.js, a Fireblocks workspace, an API user and vault, and how to get testnet USDC and gas ETH. Before you stand up an x402 facilitator or agent, set up a Fireblocks workspace and gather testnet funds. This page lists what you need and walks through getting testnet USDC and gas, the step where evaluations most often stall. Once these are in place, follow the [x402 Quickstart](/docs/x402-quickstart) to a first settled payment. ## Software * **Node.js 20 or later** for the facilitator. The [x402 Agent](/docs/x402-agent) runs on Node.js 18 or later. * **Git**, to clone [x402-facilitator](https://github.com/fireblocks/x402-facilitator) or [x402-agent](https://github.com/fireblocks/x402-agent). ## Fireblocks workspace You need a Fireblocks workspace to settle payments (selling) or to sign them (buying). If you only want to read code or run the facilitator with an empty config, you can start the server without credentials, but you cannot test signing or settle until Fireblocks is connected. ### Create a scoped API user Create a dedicated API user rather than reusing a workspace-admin key. Follow the Fireblocks guides on [creating a new API user](https://support.fireblocks.io/hc/en-us/articles/4407823826194-Adding-new-API-Users) and [choosing user roles](https://support.fireblocks.io/hc/en-us/articles/5254222799900-Best-practices-for-choosing-user-roles). The process gives you two things the facilitator and agent both need: * The **API key** (a UUID that identifies the API user). * The **API secret**, a PEM-format private key file. Save it somewhere outside your repository and restrict its permissions (`chmod 600`). For an agent, scope the API user to *Signer* (or any role limited to signing) and to a single dedicated vault. For full per-agent control, see [Fund and Control Agent Spending](/docs/x402-agent-spending-controls). > [!WARNING] > > The API key and PEM secret are production credentials. Never commit them, paste them into chat or screenshots, or bake the PEM into a container image. In any non-development deployment, keep them in a secrets manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, or HashiCorp Vault) and inject them at runtime. ### Create a vault account The facilitator settles from a vault account you own; the receiver address (`payTo`) is derived from it. An agent pays from a vault account it is scoped to. Create at least one vault account in the Fireblocks Console and note its **vault account ID** (a number, often `0` for the first). The facilitator activates the asset wallets it needs and caches the receiver addresses when you run `x402 fireblocks test --create-missing`. ## Testnet funds x402 on Fireblocks is testnet-only by default, so evaluate with valueless testnet tokens. Two parties need funding, and they need different things. | Party | Needs | Why | | - | - | - | | The payer (agent or test client) | Testnet USDC, at least the payment amount | The tokens being spent | | The payer, for `permit2` or `erc7710` first run | A few cents of testnet ETH | One-time on-chain `approve` (Permit2) or EIP-7702 self-upgrade (ERC-7710) | | The facilitator's vault | Testnet ETH on the settlement network | Gas to submit the settlement transaction | A pure `eip-3009` (USDC) payment needs no ETH on the payer side, because the payer only signs. Permit2 and ERC-7710 need a one-time on-chain setup transaction the first time, so the payer needs a little gas ETH then. ### Get testnet USDC Use the Circle faucet, which supports the testnets x402 commonly runs on: * **USDC on Base Sepolia or Ethereum Sepolia** — [faucet.circle.com](https://faucet.circle.com). Select the network, paste the receiving address, and claim. For an agent or test client, the receiving address is the payer's address. For the test client in the facilitator repo, run `npm run whoami` to print the address to fund. ### Get testnet gas (ETH) Fund the facilitator's vault, and the payer when using Permit2 or ERC-7710, with testnet ETH: * **Base Sepolia ETH** — [Alchemy Base Sepolia faucet](https://www.alchemy.com/faucets/base-sepolia) or the [Coinbase Developer Platform faucet](https://portal.cdp.coinbase.com/products/faucet). * **Ethereum Sepolia ETH** — [Alchemy Ethereum Sepolia faucet](https://www.alchemy.com/faucets/ethereum-sepolia), the [pk910 PoW faucet](https://sepolia-faucet.pk910.de) (no login), or the [Chainlink faucet](https://faucets.chain.link/sepolia). > [!NOTE] > > Most faucets require a captcha or social login by design, and each claim takes about half a minute. Fund the facilitator's vault separately from the payer: the vault pays the on-chain settlement gas, which is independent of the tokens the payer spends. ## Mainnet, when you are ready Moving real value requires opting out of the default testnet-only policy and reviewing the operator responsibilities first. Do not enable mainnet during evaluation. When you are ready, see [Network policy](/docs/x402-facilitator-operations#network-policy) and [Supported Networks and Assets](/docs/x402-networks-assets). ## What to read next * [x402 Quickstart](/docs/x402-quickstart) — use these prerequisites to settle your first payment. * [Supported Networks and Assets](/docs/x402-networks-assets) — which chains and tokens, and their asset IDs. * [Facilitator Overview](/docs/x402-facilitator-overview) — the clone-to-running quick start for sellers. # x402 Protocol Reference Source: https://developers.fireblocks.com/docs/x402-protocol-reference Reference for x402 v2 on Fireblocks: the 402 PaymentRequired body, the payment headers, facilitator request and response shapes, and the error codes. This page is a field reference for the x402 v2 messages the Fireblocks facilitator emits and accepts: the `402` body, the HTTP headers, the verify and settle payloads, and the standardized error codes. For how these fit together, see [x402 Concepts and Glossary](/docs/x402-concepts). For calling the endpoints, see [Integration](/docs/x402-facilitator-integration). ## HTTP headers | Header | Direction | Purpose | | - | - | - | | `payment-signature` | Request | Base64-encoded signed payment payload, sent by the caller on the retry after a `402`. | | `PAYMENT-RESPONSE` | Response | Returned with the `200` after settlement. Carries the settlement result (transaction hash, network, payer). | | `X-402-Integrity` | Response | Optional. The Payment Instruction Integrity envelope, mirrored from the `402` body's `integrity` field. | ## The `402` body (`PaymentRequired`) A server returns this JSON with a `402` status. The `accepts[]` array holds one entry per way to pay; the caller chooses one. ```jsonc theme={"system"} { "x402Version": 2, "error": "string", // Optional - present when describing why payment is required "resource": { "url": "https://pay.example.com/premium", "description": "string", // Optional "mimeType": "application/json" // Optional }, "accepts": [ /* PaymentRequirements, one per option */ ], "extensions": { }, // Optional - extension data "integrity": "base64url…" // Optional - Payment Instruction Integrity envelope } ``` ### PaymentRequirements Each entry in `accepts[]` fully describes one payment option. ```jsonc theme={"system"} { "scheme": "exact", // 'exact' or 'upto' "network": "eip155:8453", // CAIP-2 network id "amount": "10000", // base units of the asset (string) "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // token contract "payTo": "0x…", // recipient address "maxTimeoutSeconds": 300, // how long the quote is valid to pay "extra": { "assetTransferMethod": "eip-3009", // Optional - the transfer mechanism "name": "USD Coin", // EIP-712 domain name "version": "2" // EIP-712 domain version } } ``` ## The signed payment payload The caller signs an authorization and sends the payload base64-encoded in the `payment-signature` header. The shape of the inner authorization depends on the transfer mechanism. ```jsonc theme={"system"} { "x402Version": 2, "accepted": { /* the PaymentRequirements entry the caller chose */ }, "payload": { "signature": "0x…", "authorization": { }, // eip-3009: from, to, value, validAfter, validBefore, nonce "permit2Authorization": { }, // permit2 / upto-permit2: permitted, from, spender, nonce, deadline, witness "delegation": { } // erc7710: delegationManager, permissionContext, delegator } } ``` Exactly one of `authorization`, `permit2Authorization`, or `delegation` is present, matching the chosen mechanism. The facilitator settles all four mechanisms, but the Fireblocks [x402 Agent](/docs/x402-agent) signs only `eip-3009` and `permit2`; it does not produce `erc7710` `delegation` payloads, because Fireblocks cannot sign delegations. See [Transfer mechanisms](/docs/x402-facilitator-integration#transfer-mechanisms). ## Facilitator request and response shapes The merchant server calls the facilitator with the same body shape for both verify and settle: the signed `paymentPayload` plus the `paymentRequirements` it advertised. ```jsonc theme={"system"} // POST /api/payments/verify and POST /api/payments/settle { "x402Version": 2, "paymentPayload": { /* the signed payload above */ }, "paymentRequirements": { /* the accepts[] entry */ } } ``` **Verify response** validates the signature off-chain without moving funds: ```json theme={"system"} { "isValid": true, "payer": "0x…", "invalidReason": null } ``` **Settle response** reports the on-chain result: ```json theme={"system"} { "success": true, "transaction": "0x…", "network": "eip155:8453", "payer": "0x…", "amount": "10000" } ``` On failure, `success` is `false` and `errorReason` carries one of the error codes below. ## Supported capabilities `GET /api/payments/supported` is public and reports what the facilitator accepts: ```json theme={"system"} { "kinds": [ { "x402Version": 2, "scheme": "exact", "network": "eip155:8453" } ], "extensions": ["…"], "signers": { "…": ["…"] } } ``` ## Discovery resource shape `GET /api/discovery/resources` lists discoverable products. Each item is a resource an agent can act on: ```jsonc theme={"system"} { "resource": "https://pay.example.com/premium", "type": "http", "x402Version": 2, "accepts": [ { "scheme": "exact", "network": "eip155:8453", "amount": "10000", "asset": "0x…" } ], "lastUpdated": 1776521334, "metadata": { "category": "data", "description": "…", "mimeType": "application/json" } } ``` Only fixed-price (native-denomination) rows appear; USD-converted rows are omitted. See [Publish to the discovery catalog](/docs/x402-facilitator-product-catalog#publish-to-the-discovery-catalog). ## Error codes The facilitator returns these standardized x402 error codes in `invalidReason` (verify) or `errorReason` (settle). | Code | Meaning | | - | - | | `insufficient_funds` | The payer's balance cannot cover the authorized amount. | | `invalid_exact_evm_payload_signature` | The EIP-712 signature did not verify. | | `invalid_exact_evm_payload_authorization_valid_before` | The authorization's `validBefore` has passed (quote expired). | | `invalid_exact_evm_payload_authorization_valid_after` | The authorization's `validAfter` is in the future. | | `invalid_exact_evm_payload_authorization_value_mismatch` | The signed value does not match the advertised amount. | | `invalid_exact_evm_payload_recipient_mismatch` | The signed recipient does not match `payTo`. | | `invalid_payload` | The payment payload is malformed. | | `invalid_payment_requirements` | The supplied `paymentRequirements` are invalid. | | `invalid_network` | The network is not supported or does not match. | | `invalid_scheme` | The scheme is not supported or does not match. | | `invalid_transaction_state` | The payment is not in a state that allows this operation. | | `unexpected_settle_error` | Settlement failed for an unexpected reason. | | `unexpected_verify_error` | Verification failed for an unexpected reason. | | `unsupported_scheme` | The resolved mechanism is not offered for this product. | | `permit2_allowance_required` | The payer must grant a Permit2 allowance before paying. | ## What to read next * [Integration](/docs/x402-facilitator-integration) — the endpoints that consume and emit these shapes. * [x402 Troubleshooting and FAQ](/docs/x402-troubleshooting) — what these errors mean in practice and how to resolve them. * [Payment Instruction Integrity](/docs/x402-facilitator-operations#payment-instruction-integrity) — the `integrity` envelope and `X-402-Integrity` header. # x402 Quickstart: Your First Settled Payment Source: https://developers.fireblocks.com/docs/x402-quickstart A hands-on testnet walkthrough that wires the Fireblocks x402 facilitator, an example merchant, and a test client into one real settled payment. This tutorial takes you from a fresh clone to a real, on-chain settled x402 payment on a testnet. You run three pieces: the **facilitator** (verifies and settles), an **example merchant** (gates a paid endpoint), and a **test client** (pays for it). By the end, a request to a paid endpoint settles a USDC transfer through your Fireblocks vault and returns the resource. Set aside about 30 minutes. Everything here uses testnet funds with no real value. ## Before you start Complete [Prerequisites and Testnet Funds](/docs/x402-prerequisites) first. You need: * Node.js 20 or later, and Git. * A Fireblocks API key and PEM secret from a scoped API user, plus a vault account ID. * Testnet USDC for the payer, and testnet ETH for gas in both the payer (for non-`eip-3009` runs) and the facilitator's vault. The example merchant and test client live in the [x402-facilitator](https://github.com/fireblocks/x402-facilitator) repo under `examples/`, so a single clone covers all three pieces. ## Step 1: Start the facilitator ```bash theme={"system"} git clone https://github.com/fireblocks/x402-facilitator cd x402-facilitator npm install # Scaffold config/facilitator.json + secrets/jwt-hs256.key npm run setup ``` Open `config/facilitator.json` and fill in your Fireblocks credentials: ```jsonc theme={"system"} "fireblocks": { "api_key": "", // the API user UUID "api_secret_path": "./secrets/fireblocks.pem", // path to your PEM secret "receiver_vault": "0" // your vault account ID } ``` Start the facilitator: ```bash theme={"system"} npm run dev # facilitator on http://localhost:3000 ``` Confirm it is up: ```bash theme={"system"} curl http://localhost:3000/api/health # {"status":"ok","role":"all","mechanisms":[...], …} ``` > [!WARNING] > > Never commit `config/facilitator.json` with real credentials or the PEM secret. The repository's `.gitignore` excludes both by default. In any non-development deployment, keep them in a secrets manager and inject at runtime. ## Step 2: Install the CLI and mint an admin token In a second terminal, build the `x402` CLI and put it on your PATH: ```bash theme={"system"} npm run build && npm link # puts `x402` on your PATH (one-time) ``` Mint a local admin token and export it so the CLI is authenticated: ```bash theme={"system"} npm run setup:admin-token -- --preset full --ttl 2h export X402_ADMIN_TOKEN= ``` ## Step 3: Activate the vault and import USDC Activate the vault wallets the facilitator needs and cache the receiver addresses: ```bash theme={"system"} x402 fireblocks test --create-missing ``` Import testnet USDC as a payable asset: ```bash theme={"system"} x402 assets import USDC_BASECHAIN_ETH_TEST5_8SH8 \ --transfer-mechanism eip-3009 \ --eip712-name "USDC" --eip712-version 2 \ --stable ``` ## Step 4: Declare a product and mint a merchant key Declare the paid endpoint as a product, priced at one US cent: ```bash theme={"system"} x402 products add --name "Premium" --endpoint /premium \ --usd-price 0.01 --asset USDC_BASECHAIN_ETH_TEST5_8SH8 ``` Copy the `product_id` it prints (`prod_…`). Then mint an API key the merchant server will use to call the facilitator: ```bash theme={"system"} x402 keys create --scopes process-payments --label merchant # → prints the key once; copy it now ``` > [!WARNING] > > The merchant API key plaintext is shown only once; only a hash is stored. Copy it now, treat it as a secret, and never commit or expose it. If you lose it, revoke it and mint a new one. ## Step 5: Allow the facilitator to settle The facilitator settles by submitting a `CONTRACT_CALL` transaction from your vault. For that transaction to go through without a manual approval each time, add a Fireblocks Policy rule that auto-approves a `CONTRACT_CALL` from your receiver vault using the network's gas asset (for example, `ETH_TEST5` on Sepolia). See [Set Policies](/docs/set-transaction-authorization-policy). Without an auto-approving Policy rule, settlement transactions wait for manual approval in the Fireblocks Console, and the quickstart will appear to hang at settlement. ## Step 6: Run the example merchant In a third terminal, configure and start the example merchant with the key and product ID from step 4: ```bash theme={"system"} cd examples/merchant npm install cp .env.example .env ``` Set these in `examples/merchant/.env`: ```bash theme={"system"} FACILITATOR_URL=http://localhost:3000 FACILITATOR_API_KEY= PREMIUM_PRODUCT_ID= SETTLEMENT_MODE=optimistic # Optional - 'optimistic' or 'settle-first' ``` ```bash theme={"system"} npm run dev # example merchant on http://localhost:3010 ``` The merchant gates `/premium` and leaves `/hello` free. A request to `/premium` without payment now returns a `402`. `SETTLEMENT_MODE` controls whether the merchant settles synchronously before serving the response (`settle-first`) or serves first and settles in the background (`optimistic`). It is an important production choice with a latency-versus-risk tradeoff; see [When to settle](/docs/x402-facilitator-integration#when-to-settle). ## Step 7: Pay for the resource In a fourth terminal, set up the test client. It signs payments with a local development wallet, so you do not need a second Fireblocks vault for the payer. ```bash theme={"system"} cd examples/test-client npm install # Generate a dev key (or use one you already have) export PRIVATE_KEY=$(node -e "console.log(require('ethers').Wallet.createRandom().privateKey)") # Print the address to fund, then fund it with testnet USDC (see Prerequisites) npm run whoami ``` Fund the printed address with testnet USDC from [faucet.circle.com](https://faucet.circle.com). Then make the payment: ```bash theme={"system"} MECHANISM=eip3009 CHAIN=84532 npm run dev ``` The client requests `/premium`, receives the `402`, signs an EIP-3009 authorization, retries with a `payment-signature` header, and prints the merchant's response plus the settlement transaction hash from the `PAYMENT-RESPONSE` header. ## Step 8: Confirm settlement Check the payment landed, from the CLI terminal: ```bash theme={"system"} x402 payments list --limit 5 x402 payments get # full record, with the on-chain tx hash ``` A `completed` status with a transaction hash means the USDC moved on-chain into your vault's receiver address. You have run a full x402 payment. ## Run every mechanism at once The repo ships an end-to-end harness that exercises `eip-3009`, `permit2`, and `erc7710` in sequence, polling until each payment reaches `completed`: ```bash theme={"system"} npm run e2e ``` It assumes the facilitator and merchant are running, the auto-approving Policy rule from step 5 is in place, and the payer EOA is funded with testnet USDC and a little ETH (Permit2 and ERC-7710 each need a one-time on-chain setup transaction). The harness prints a pass/fail summary with block-explorer links and is safe to re-run. ## Troubleshooting * **Settlement hangs.** You are likely missing the auto-approving Policy rule from step 5, so the `CONTRACT_CALL` is waiting for manual approval in the Console. * **`insufficient_funds` or a fund-me block.** The payer address is short on USDC, or short on gas ETH for a Permit2 or ERC-7710 first run. Top it up from a faucet. * **Server refuses to boot citing mainnet.** You imported a mainnet asset. The facilitator is testnet-only by default. Use a testnet asset, or see [Network policy](/docs/x402-facilitator-operations#network-policy). For more, see [x402 Troubleshooting and FAQ](/docs/x402-troubleshooting). ## What to read next * [Integration](/docs/x402-facilitator-integration) — replace the example merchant with your own server. * [Build an Agentic Product Catalog](/docs/x402-facilitator-product-catalog) — add more products and publish them to agents. * [x402 Agent](/docs/x402-agent) — pay for resources from a Fireblocks vault instead of a local wallet. # x402 Troubleshooting and FAQ Source: https://developers.fireblocks.com/docs/x402-troubleshooting Resolve common x402 issues across the Fireblocks facilitator and agent, and answers to frequent questions about cost, custody, networks, and mechanisms. Common problems and questions when running x402 on Fireblocks, covering both the [facilitator](/docs/x402-facilitator-overview) (selling) and the [agent](/docs/x402-agent) (buying). For exact message shapes and error codes, see the [Protocol Reference](/docs/x402-protocol-reference). ## Facilitator issues **The server refuses to boot, citing mainnet.** A configured asset is a mainnet asset and the testnet-only policy is in force. Use a testnet asset, or opt in deliberately with `X402_ALLOW_MAINNET=true`. The error lists the offending assets. See [Network policy](/docs/x402-facilitator-operations#network-policy). **Payments reach `settling` and never complete.** The settlement `CONTRACT_CALL` is waiting for manual approval in the Fireblocks Console. Add a Policy rule that auto-approves the `CONTRACT_CALL` from your receiver vault using the network's gas asset. See [Set Policies](/docs/set-transaction-authorization-policy). To reconcile rows that are already stuck, run `x402 payments sync ` or `x402 payments sync-all`. **An asset I imported does not appear in the `402` quote.** The asset cannot be priced. Mark stablecoins `stable: true`, or give other assets a `price_symbol`. Unpriceable assets are dropped from the response rather than rejected, so other assets stay available. See [PriceProvider](/docs/x402-facilitator-integration#priceprovider). **A payment is rejected with `unsupported_scheme`.** The resolved transfer mechanism is not in the product's pricing rows, or the request did not originate through `/api/payments/create` and is missing `extra.productId`. Offer the mechanism on the product, or route the quote through create. See [Picking a mechanism](/docs/x402-facilitator-integration#picking-a-mechanism). **A settlement failed but the funds may have moved.** Once a Fireblocks transaction ID is attached to a payment, the facilitator does not force the row to `failed` on a transient error, because the transaction may still land. Let the reconciler decide from Fireblocks's own state, or run `x402 payments sync `. See [Inspecting payments](/docs/x402-facilitator-operations#inspecting-payments). ## Agent issues **`no supported payment mechanism in accepts[]`.** The server only offered a mechanism the agent cannot sign, such as ERC-7710. Ensure the server offers `eip-3009` or `permit2`. The agent does not sign ERC-7710, because Fireblocks cannot sign delegation payloads. **`ERROR_TYPED_MESSAGE_TO_PROTECTED_7702_CONTRACT`.** A Fireblocks Policy rule blocked the signature. Add a typed-message Policy rule that allows this typed data for the agent's vault. See [Set Policies](/docs/set-transaction-authorization-policy). **`integrity check failed`.** The Payment Instruction Integrity envelope expired, the `did:web` document was unreachable, or the signature did not verify. Check the facilitator's clock skew and that the DID document is served, or relax to `VERIFY_INTEGRITY` if you are not enforcing integrity. See [Payment Instruction Integrity](/docs/x402-facilitator-operations#payment-instruction-integrity). **Payment expired.** Too much time passed between the `402` and the signed retry. Re-request to get a fresh quote, and reduce signing latency. Settlement quotes are valid for `maxTimeoutSeconds`. **Invalid signature.** The EIP-712 domain did not match. Verify the `name` and `version` in the quote's `extra`, and that the chain ID matches the asset's network. **The agent will not pay, reporting insufficient balance.** The vault is short of the spend asset. Fund it (see [Fund and Control Agent Spending](/docs/x402-agent-spending-controls)), or, only for testing, disable the check with `check_balance: false`. ## Frequently asked questions **Does my customer traffic pass through the facilitator?** No. Your server serves its own traffic and returns the `402` itself. It calls the facilitator only to verify and settle. The facilitator is API-only. **Who pays the on-chain gas?** The facilitator's vault pays settlement gas. The payer signs off-chain and spends no gas at signing time, except for a one-time setup transaction the first time it uses Permit2 or ERC-7710. **Can I accept tokens other than USDC?** Yes. USDC uses the `eip-3009` mechanism; any other ERC-20 settles through the `permit2` or `upto-permit2` mechanisms. See [Transfer mechanisms](/docs/x402-facilitator-integration#transfer-mechanisms). **Can I charge a variable amount?** Yes, with the `upto` scheme and the `upto-permit2` mechanism, where the payer signs a ceiling and the facilitator settles up to it. Your pricing logic is responsible for staying within the signed maximum. **How do I limit what an agent can spend?** Spending limits live in Fireblocks Policy rules (per-transaction caps, velocity caps, destination allowlists, approval thresholds), not in the agent code. See [Fund and Control Agent Spending](/docs/x402-agent-spending-controls). **Is mainnet supported?** Yes, but it is off by default. The facilitator is testnet-only until you set `X402_ALLOW_MAINNET=true`. Review the operator responsibilities first. See [Network policy](/docs/x402-facilitator-operations#network-policy). ## What to read next * [x402 Protocol Reference](/docs/x402-protocol-reference) — the full error-code table and message shapes. * [x402 Quickstart](/docs/x402-quickstart) — a known-good end-to-end setup to compare against. * [Operating and Production](/docs/x402-facilitator-operations) — payment lifecycle, reconciliation, and the management API. # Build with Fireblocks Source: https://developers.fireblocks.com/index Guides, API reference, SDKs, and AI-ready tools like the Documentation MCP and CLI. Everything you need to go from first API call to production. [![Fireblocks Developers](/images/fb-devs.svg)](/) Search or ask AI ⌘K [Try in Sandbox](https://www.fireblocks.com/developer-sandbox-sign-up) [Sign in →](https://console.fireblocks.io/v2/) Documentation sections # Build with Fireblocks Guides, API reference, SDKs, and AI-ready tools like the Documentation MCP and CLI. Everything you need to go from first API call to production.
  1. 1 ### Add the MCP Ground your IDE or agent in the official Fireblocks docs. Choose your agent
  2. 2 ### Install the CLI A typed command for every API operation. Node.js 18+. \$ npm install -g @fireblocks/fireblocks-cli Copy command to clipboard
  3. 3 ### Ask your agent about Fireblocks It reads the docs and runs the CLI for you.
[Full Quickstart Guide →](/docs/quickstart) [Explore Key Features →](/docs/capabilities) ## Build by use case Customer Stories See how engineering teams at leading institutions build and scale on Fireblocks. View stories → [Open](https://www.fireblocks.com/customers) ## Resources for developers Everything you need to get from zero to your first production transaction. ## Embedded wallets with Dynamic Dynamic, a Fireblocks company, builds non-custodial embedded wallets with sub-second signing, cross-chain support, and flexible recovery across web, iOS, and Android. [Browse the full Dynamic docs →](https://www.dynamic.xyz/docs) ## What's happening Activity filters ## Ready to build? [Try in Sandbox →](https://www.fireblocks.com/developer-sandbox-sign-up) [Quickstart →](/docs/quickstart) [Privacy Policy](https://www.fireblocks.com/privacy-policy/) [Cookie Policy](https://www.fireblocks.com/cookie-policy/) [Terms of Use](https://www.fireblocks.com/terms-of-use/) [Fireblocks Regulated Entities](https://www.fireblocks.com/fireblocks-regulated-entities/) ![CCSS, SOC 2, ISO 27001, ISO 27017, ISO 27018, ISO 22301 certifications](/images/footer-badges.svg) # Add Tokens Source: https://developers.fireblocks.com/reference/add-your-tokens Fireblocks allows clients to add tokens across various blockchains via the Fireblocks API. To register a new token, use the [Register a New Asset](/reference/registernewasset) endpoint with the appropriate parameters for the supported blockchain. The blockchains supported for self-token addition via the API include: * EVM-based chains * Algorand * NEAR * Solana * Stellar * Sui * TON * TRON The API requires a `POST` request with the following parameters: * **blockchainId:** The unique network identifier (e.g., `ETH`). * **address:** The asset address: * **EVM-based chains:** Token contract address * **Algorand (ALGO):** Asset ID * **NEAR:** Token address * **Solana:** Token's mint account address * **Stellar (XLM):** Issuer address * **Sui:** The token's type * **TON:** Token address * **TRON (TRX):** Token contract address * **symbol:** Required for XLM tokens only ## Code example Below is a code example for adding a new token to your workspace: ```javascript theme={"system"} import { readFileSync } from 'fs'; import { Fireblocks, BasePath } from "@fireblocks/ts-sdk"; const FIREBLOCKS_API_SECRET_PATH = ""; // Initialize a Fireblocks API instance with local variables const fireblocks = new Fireblocks({ apiKey: "", basePath: BasePath.US, secretKey: readFileSync(FIREBLOCKS_API_SECRET_PATH, "utf8"), }); (async() => { const addTokenRes = await fireblocks.blockchainsAssets.registerNewAsset({ registerNewAssetRequest: { blockchainId: "ETH", address: "0xdBA8e8021FE321af91FC3A08e223EF15908cB2bB" } }) console.log(JSON.stringify(addTokenRes, null, 2)) })(); ``` In this example, we are adding the`0xdBA8e8021FE321af91FC3A08e223EF15908cB2bB` token (Smart Contract address) on the Ethereum network. # About Address Registry Source: https://developers.fireblocks.com/reference/address-registry Look up the legal entity behind a blockchain address, and manage your Address Registry participation through the Fireblocks API. Address Registry maps blockchain addresses in the Fireblocks network to the legal entities that control them. It is free, on by default for every workspace, and works on consent: while your workspace participates, other participating customers can look up your addresses and you can look up theirs. Results come from registered data, not transaction heuristics. To appear as a verified entity, register your LEI in [Legal Entity Setup](/reference/legal-entity-setup); to accept or reject transactions based on the counterparty, see [About Address Registry Screening](/docs/address-registry-screening). ## What you can do through the API | Task | Console | API | | - | - | - | | Look up an address | Yes | [`GET /address_registry/legal_entities/{address}`](/api-reference/compliance/look-up-legal-entity-by-blockchain-address) | | Get the workspace's participation status | Yes | [Get participation status](/api-reference/compliance/get-address-registry-participation-status-for-the-authenticated-workspace) | | Opt the workspace in | Yes | [`POST /address_registry/tenant`](/api-reference/compliance/opt-the-workspace-in-to-the-address-registry) | | Opt the workspace out | Yes | [`DELETE /address_registry/tenant`](/api-reference/compliance/opt-the-workspace-out-of-the-address-registry). A successful response returns `OPTED_OUT`. | | List vault accounts opted out | Yes | [List vault-level opt-outs](/api-reference/compliance/list-vault-level-address-registry-opt-outs-paginated) | | Opt vault accounts out | Yes | [Add vault accounts to the opt-out list](/api-reference/compliance/add-vault-accounts-to-the-address-registry-opt-out-list) | | Opt out at account level (all current and future workspaces) | Through your Customer Success Manager | No | | Register a legal entity and map vault accounts | Yes | See [Legal Entity Setup](/reference/legal-entity-setup) | | Export a proof of ownership document | Yes | See [Export a Proof of Ownership Document](/docs/export-proof-of-ownership) | ## Look up an address Call the lookup endpoint. URL-encode the address when needed. You can look up any address: there's no requirement to have an active or pending transaction with it. Only workspaces that participate in Address Registry can query it. ```ts theme={"system"} const { data } = await fireblocks.compliance.getLegalEntityForAddress({ address: "0x742d35cc6634c0532925a3b844bc9e7595f0beb0", }); if (data.leiData) { console.log(data.entityName, data.jurisdiction, data.lei); } ``` | Field | Description | | - | - | | `leiData` | `true` when the result is verified — the counterparty declared an LEI, the entity data came from GLEIF, and Fireblocks' compliance team confirmed the legal entity name matches Fireblocks' own client records. `false` if any part of that chain didn't happen. | | `entityName` | The legal entity's name. | | `jurisdiction` | ISO 3166-1 alpha-2 country code. | | `lei` | The Legal Entity Identifier. May be empty when `leiData` is `false`. | | `travelRuleProviders` | The Travel Rule providers the entity declared, for example `TRAVEL_RULE_PROVIDER_NOTABENE`, `TRAVEL_RULE_PROVIDER_SYGNA`, `TRAVEL_RULE_PROVIDER_TRISA`, or `TRAVEL_RULE_PROVIDER_OPENVASP`. | | `email` | The entity's Travel Rule contact email, when available. | | `proofOfOwnershipAvailable` | Whether you can currently create a proof of ownership document for this address; see [Export a Proof of Ownership Document](/docs/export-proof-of-ownership). | There's no separate field for "verified" — `leiData` is that signal. Anything else is based on Fireblocks data without that independent check. A lookup returns information about the legal entity only: its name, jurisdiction, and LEI if declared. It never returns anything about that entity's own customers or account holders, and the registry holds no link between vault addresses and identified individuals. The lookup returns `not_found` when the counterparty isn't a Fireblocks customer, has opted out, or is on an unsupported environment. A `not_found` result doesn't mean the address is unhosted, illicit, or non-compliant. You can only look up a specific address: you can't enumerate the registry or query in reverse. To prevent bulk data extraction, lookups are also authenticated, rate limited, capped daily, and monitored, so the number of queries stays proportional to your transactions. See [Acceptable use](#acceptable-use). ## Manage participation You can opt out your whole workspace, or only specific vault accounts, at any time, and opt back in later. Opting out has three effects: * Lookups against your opted-out addresses return `not_found`. * A workspace that has opted out can't look up addresses itself. * Compliance workflows built on Address Registry, including [About Address Registry Screening](/docs/address-registry-screening), stop working until you opt back in. To exclude all current and future workspaces in your account, contact your Customer Success Manager. ## Limitations * **European and Swiss-hosted workspaces** aren't supported. Lookups against addresses from these workspaces return `not_found`. * **Developer Sandbox workspaces** don't support Address Registry. * **Coverage** is limited to addresses within the Fireblocks network. ## Acceptable use Address Registry exists only to verify counterparties in connection with digital asset transactions. You must not: * Use it for any other purpose. * Scrape, bulk-download, or systematically extract data from it. * Resell, redistribute, or commercialize query results, or any data obtained from it. * Attempt to identify, profile, or de-anonymize individual people using its data. * Use it for surveillance or competitive intelligence. * Circumvent technical controls, authentication requirements, or rate limits. Fireblocks monitors usage for misuse, and can suspend or revoke access, with or without notice, if it detects abuse. Your use must comply with your agreement with Fireblocks. You should also assess your own data protection obligations for participating. ## On the Help Center To look up addresses or manage participation in the Console, see [About Address Registry](https://support.fireblocks.io/hc/en-us/articles/30614328858652-About-Address-Registry). # Android SDK errors Source: https://developers.fireblocks.com/reference/android-sdk-errors | Error Code | Error Message | Description | | - | - | - | | 102 | Invalid Certificate | Invalid Certificate, it might be expired or the device clock is out of sync | | 106 | Missing algorithms | Indicates that `com.fireblocks.sdk.Fireblocks.generateMPCKeys` was called with an empty set of algorithms. | | 108 | Incomplete device setup | Indicates that our device setup is incomplete. For example if we generated a key but we didn't perform backup | | 107 | Missing private keys | The client failed to load the keys' content provided by the `com.fireblocks.sdk.keys.FireblocksKeyStorage` implementation. This implementation is provided to `com.fireblocks.sdk.Fireblocks` via the initialization method. | | 110 | Invalid physical device id | Indicates that we have an invalid physical device id. Please check if your device was recovered from a different physical device | | 111 | Max devices per wallet reached | The maximum number of devices in a wallet is reached while trying to add a new device. | | 200 | Timeout during key creation, didn't get startup message | During key creation, several messages from the backend are handled. The first message did not arrive in the expected amount of time. | | 201 | Unknown algorithm | Indicates that `com.fireblocks.sdk.Fireblocks.generateMPCKeys` was called with an invalid algorithm. | | 202 | Failed to generate key, key exists in server but not on the device | The metadata of the key was previously sent to the backend, but the key is no longer on the client. | | 203 | Timeout during key creation | During key creation, several messages from the backend are handled. One of the second-to-last messages did not arrive in the expected amount of time. | | 204 | Failed to send public key | The public key failed to back up with the backend after key creation. | | 205 | Failed to request key | One or more of the key requests failed to initiate. | | 206 | Failed to enroll player | The process to register the `deviceId` with the backend failed. | | 207 | Failed to create key | During key creation, several messages from the backend are handled. One of the messages failed to process on the device. | | 208 | Failed to confirm key | Used in `com.fireblocks.sdk.Fireblocks.generateMPCKeys` during key creation, a key was not fully confirmed in the backend | | 300 | Failed to request end-user takeover | A failure to receive keys for takeover from the backend. | | 301 | Failed to takeover keys | There was an error while processing the takeover request on all keys. | | 302 | Timeout during key takeover | A failure to receive keys for takeover from the backend in the expected amount of time. | | 400 | Failed to export keys | The client failed to export all the available keys. | | 401 | Missing public keys | Some of the data needed to export the key is unavailable. | | 402 | Missing public key | The client failed to load the keys provided by the `com.fireblocks.sdk.keys.FireblocksKeyStorage` implementation. This implementation is provided to `com.fireblocks.sdk.Fireblocks` via the initialization method. | | 403 | Failed to derive asset key | Deriving the asset key failed. | | 404 | Missing cloud private keys | The cloud key share is not the full key share. | | 405 | Missing chain code | The relevant chain code is missing. | | 406 | Missing private key | The client failed to load the keys provided by the `com.fireblocks.sdk.keys.FireblocksKeyStorage` implementation. This implementation is provided to `com.fireblocks.sdk.Fireblocks` via the initialization method. | | 407 | Failed to export key, recovered PublicKey is not equal to the original PublicKey | The recovered key is not equal to the original key. | | 408 | Failed to export key | The key export failed. The reason for failure may be located in the logs. | | 500 | Failed to sign transaction, unknown txId | Indicates that `com.fireblocks.sdk.Fireblocks.signTransaction` was called with an invalid transaction ID. | | 501 | Error during transaction signing creation, didn't get start signing message | During transaction signing, several messages from the backend are handled. The first message failed to arrive in the expected amount of time. | | 502 | Failed to sign transaction | The message processing failed. | | 503 | Timeout during transaction signing | During transaction signing, several messages from the backend are handled. One of the second-to-last messages failed to arrive in the expected amount of time. | | 504 | Transaction signing was stopped | Used in `com.fireblocks.sdk.Fireblocks.signTransaction` Indicates that the transaction signing was stopped | | 600 | Backup not available | There are no keys to recover from the backend. | | 601 | Failed to recover keys | The key recovery process failed. | | 603 | Wrong passphrase | The provided passphrase is wrong | | 700 | Failed to get key IDs | The client failed to fetch the key's metadata from the backend. | | 701 | Missing key IDs in server for backup | The client failed to fetch the key's metadata from the backend. | | 702 | We have a discrepancy between valid key IDs between server and client | A discrepancy exists between the `keyId` parameters stored on the client and the backend. | | 703 | Failed to backup keys, missing keys | The client failed to fetch the key's metadata from the backend. | | 704 | Failed to backup key | The key backup process with the backend failed. | | 705 | Invalid passphrase error | The used passphrase is invalid. | | 800 | Invalid add device setup data | The request to add a device failed due to invalid data. | | 900 | Failed to join wallet | The request to join an existing wallet failed. | | 901 | Timeout during join wallet | The request to join an existing wallet timed out. | | 902 | Join wallet was stopped | The request to join an existing wallet was stopped. | | 1000 | Failed to approve join wallet | The request to approve a new device joining an existing wallet failed. | | 1001 | Timeout during approve join wallet | The request to approve a new device joining an existing wallet timed out. | | 1002 | No keys to provision | Indicates that `com.fireblocks.sdk.Fireblocks.approveJoinWalletRequest` was called with an empty set of algorithms. | | 1003 | Approve join wallet was stopped | The request to approve a new device joining an existing wallet was stopped. | # API Endpoints Overview Source: https://developers.fireblocks.com/reference/api-endpoints-overview The **API Endpoints** section lists every REST endpoint in the Fireblocks API. Each endpoint page includes: * **Description** — What the endpoint does and when to use it * **Parameters and request body** — Required and optional fields * **Response schema** — Response shape and status codes * **Code samples** — Copyable request examples in multiple languages, plus an example response The reference is generated from our [OpenAPI 3.0 specification](https://swagger.fireblocks.com/openapi.yaml). You can import the spec into Swagger UI, ReDoc, Postman, or use it to generate client libraries. ## Before you call the API * **[Authentication](/reference/signing-a-request-jwt-structure)** — Sign requests with JWT using your API key and secret * **[Rate limiting](/reference/rate-limiting)** — Request limits and best practices ## Testing your calls Each endpoint page shows copyable request examples and an example response, not an interactive playground. To test a call against your workspace, use the [Postman Guide](/reference/explore-postman-collection). ## Next steps Use the sidebar to browse endpoints by category, or search for a specific operation. For high-level concepts and SDKs, see the [API Overview](/reference/api-overview). # API responses and error codes Source: https://developers.fireblocks.com/reference/api-error-codes # HTTP status code summary | HTTP Status Code Summary | | | | - | - | - | | 200 | OK | The request was processed as expected. | | 400 | Bad Request | The request is not well-formed, violates the schema, or has incorrect fields. | | 401 | Unauthorized | The API key doesn't match the signature or have the necessary permissions to perform the request. | | 403 | Forbidden | The API key doesn't have the necessary permissions to complete the request. | | 404 | Not Found | The requested resource doesn't exist. | | 429 | Too Many Requests | Too many requests hit the API too quickly. Blocked due to rate limiting. We recommend an exponential backoff of your requests. | | 5XX | Server Errors | Something went wrong on Fireblocks' end. (These are rare.) | # API error codes The tables below contain all error codes (along with their corresponding HTTP status codes) for the Fireblocks API. We recommend reviewing the codes related to the features you want to implement for your workspace and customers. ## 4XX Error Codes | Error Code | HTTP Status Code | Relevant Features | Issue and Remediation | | - | - | - | - | | -1 | 401 | All | **Invalid signature** If you receive this error message, ensure the private key you used is correct. | | -2 | 401 | All | **Missing API Key in the JWT** Verify that you specify the API key | | -3 | 401 | All | **The entire JWT is missing** - Uncommon\_ - Potential code change, or bad parameters passed to Fireblocks SDK constructor | | -4 | 401 | All | **Wrong URL in the JWT** - Uncommon\_ - Potential code change to the Fireblocks SDK source code on your device | | -5 | 401 | All | **Unauthorized: Invalid token format** This error code typically indicates the certificate associated with the API user making the call has expired. | | -6 | 401 | All | **Wrong User in the JWT** The user being used as part of the request is incorrect. Verify that the correct user is being used | | -7 | 401 | All | **Unauthorized: Token was not accepted** This error code typically indicates that the JSON Web Token (JWT) supplied with the API user’s request was not accepted. API requests to Fireblocks are authenticated using the provided JWT. If the JWT is invalid, you will receive the error code. The cause of the error is likely either the wrong API key being used or the wrong baseURL being used. For SDK users, this typically means an incorrect API key was used to create the client. For users with native API client implementations, this typically indicates an error with your JWT creation logic. | | -8 | 401 | All | **Insufficient permissions** This error code typically indicates that the API user initiating this request does not have access to the requested operation based on their user role | | -10 | 401 | All | **The request has expired** This error typically indicates the JSON Web Token (JWT) supplied with the API user’s request has timed out. Please note: - For Fireblocks SDK users, the JWT expiration time is defined based on the system time. - For users with their own API client implementations, the JWT data is defined by the user’s implementation. | | -12 | 401 | All | **The request is with a future issuance timestamp (iat)** This error code typically indicates the JSON Web Token (JWT) supplied with the API user’s request is not yet valid for processing. - For SDK users, the JWT “initiated–at” time is defined based on the system time. - For users with their own API client implementations, the JWT data is defined by the user’s implementation. | | 900 | 400 | All, Trading | **Validation Error** The request contains invalid data or parameters. | | 1000 | 400 | Vault | **GetVaultAccounts request has invalid values** Check that you pass a valid `minAmountThreshold` (if you pass it) and only one of `namePrefix`, `nameSuffix` if you pass it | | 1002 | 400 | Vault | **Create Vault Account failed** Verify that the values provided to the create vault account request are valid and conform to the types specified in the API Docs | | 1004 | 404 | Vault | **Vault account ID was not found** Verify that the vault account you're trying to query is valid and exists | | 1006 | 404 | Asset, Balance | **Asset doesn't exist** The asset requested for the given vault account does not exist. Verify that the vault account contains the asset you're requesting. | | 1008 | 400 | Wallet, Asset (EOS Specific) | **EOS Wallet Creation failed** The eosAccountName parameter is invalid | | 1010 | 400 | Wallet | **Renaming a deposit address failed** You're trying to add an address to a blockchain that does not support more than 1 address (such as ETH) | | 1013 | 400 | Vault | **Vault account renaming failed** Verify that the parameters passed to the vault account renaming operation are valid | | 1015 | 400 | Wallet | **Rename a deposit address failed** Verify that the parameters that were passed are valid and that the asset you're trying to rename exists | | 1016 | 400 | Wallet | **Rename a deposit address failed** The rename operation failed due to a backend issue - open a ticket to support | | 1017 | 400 | Vault | **Hide vault account failed** The vault account you requested to hide does not exist. Please verify you're using the correct vault account ID | | 1019 | 400 | Vault | **Hide on a hidden vault account request** You're requesting to hide a vault account that is already hidden. Please verify you're using the correct vault account ID | | 1020 | 400 | Vault | **Unhide vault account failed** The vault account you requested to unhide does not exist. Please verify you're using the correct vault account ID | | 1022 | 400 | Vault | **Unhide on a visible vault account request** You're requesting to unhide a vault account that is not hidden. Please verify you're using the correct vault account ID | | 1023 | 400, 404 | Wallet | **Get legacy address failed** There is no legacy address used for this specific asset | | 1024 | 400, 404 | Wallet | **Get legacy address for an asset without one** The asset you're trying to get the legacy address for does not have one (for example, ETH) | | 1025 | 400 | Asset, Vault, Wallet | **Usage of an unsupported asset** The asset you specified in your request is invalid. Please check it against the supported asset list | | 1026 | 400 | Asset, Vault, Wallet | **Creation of a wallet that already exists** You've requested to create a wallet for an asset that already has a wallet in the vault account. Please check whether you're using the correct vault account and the correct asset | | 1027 | 400 | Asset, Balance | **Requested max spendable amount for asset that doesn't support that request** You've requested to know the max spendable amount for an asset, which does not support this request. Verify you're using one of the assets according to the API docs | | 1028 | 400 | Asset, Balance | **Max Spendable Amount invalid value** The parameters you provided for the request are invalid. Please verify you provided the correct type of values, as well as a valid asset and vault account ID | | 1031 | 400 | Asset, Balance | **Get balance by assets** The request to balance of assets in all vault accounts is invalid, only one of `accountNamePrefix` and `accountNameSuffix` can be provided | | 1032 | 400, 404 | Asset, Balance | **Get balances by assets or get balance for asset failed** The asset requested does not exist in the vault account | | 1034 | 400 | Wallet | **Create testnet asset failed** You're requested to create a testnet asset on a workspace that does not have testnet support | | 1035 | 400 | Asset | **Activate wallet failed** The request to activate the wallet failed. Please open a ticket to support | | 1037 | 400 | Vault | **Get vault accounts endpoint is disabled** Workspaces created after May 2022 do not support the get vault accounts endpoint; use the paged request | | 1038 | 400 | Wallet, Asset | **Adding a token without base asset** Before adding a token to the vault account, please create a base asset wallet | | 1039 | 400 | Wallet, Asset | **Insufficient base asset for token wallet** Before creating a token asset, make sure you have sufficient base asset to cover token transactions | | 1040 | 401 | Vault | **Unauthorized user to create vault account** The user who is trying to add a vault account does not have the proper permissions needed to create a vault account. Make sure you use the correct user | | 1048 | 400 | Vault | **The vault account type is not supported by the workspace** You attempted to create/use a vault account with a type that isn’t enabled or recognized for your tenant (plan/feature flag/environment). Use a supported type (or omit it to use the default), or contact Fireblocks to enable the feature. | | 1103 | 400 | Exchange, Fiat | **No such exchange/fiat account ID** The exchange or fiat account ID does not exist. Make sure you're using the correct one | | 1105 | 400 | Exchange | **Exchange account for asset by ID does not exist** The exchange account's asset does not exist for the specified exchange account ID. Make sure you're using the correct exchange account ID and asset | | 1106 | 400 | Exchange Transfers | **No such exchange account ID** The exchange account ID specified does not exist | | 1108 | 408 | Exchange | **Exchange timeout when adding an account** We faced an internal timeout when trying to add the exchange account | | 1111 | 400 | Exchange Transfers | **Exchange convert failed** Failed to perform a convert operation. Please check the message reply to understand the issue | | | | | | | 1202 | 400 | Internal Wallets | **Create internal wallet failed** Internal wallet name must be specified, not empty, and to not already exist in a different wallet | | 1204 | 400 | Internal Wallets | **Get internal wallet by id failed** The internal wallet specified does not exist. Make sure you're querying the correct internal wallet ID | | 1206 | 400 | Internal Wallets | **Delete internal wallet failed** Failed to delete the internal wallet. Please make sure you've specified the correct internal wallet parameters | | 1208 | 400 | Internal Wallets | **Create an internal wallet failed** Failed to create an internal wallet. Please verify you've provided the correct parameters for the creation as per the API docs | | 1209 | 400 | Internal Wallets | **Getting the wallet's asset failed** The internal wallet based on the ID and asset specified does not exist. Please make sure you're using the correct asset and wallet ID | | 1212 | 400 | Internal Wallets | **Deleting specific asset from wallet failed** Please verify that you specified the correct wallet ID and asset ID for deletion | | 1213 | 400 | Internal Wallets | **Create an unsupported asset address in a wallet** The asset you're trying to add to the wallet is not supported. Verify the asset you're trying to use | | 1214 | 400 | Internal Wallets | **Create an asset from a different network in the wallet** The asset you're trying to add to the wallet is in a different network (mainnet or testnet), and your workspace does not support this network. Please verify the network of the asset and that your workspace correlates | | 1215 | 400 | Internal Wallets | **Add an asset in a wallet that already has it** You're trying to add an asset to the internal wallet, but it already has this asset. Please make sure you're adding a new asset and not one that already exists | | 1216 | 400 | Internal Wallets | **Add an asset with an invalid address to the wallet** You're trying to add an asset to the internal wallet, but the address does not match the asset's conventions. Please check the address you're trying to add | | | | | | | 1302 | 400 | External Wallets | **Create external wallet failed** External wallet name must be specified, not empty, and to not already exist in a different wallet | | 1304 | 400 | External Wallets | **Get external wallet by Id failed** The external wallet requested does not exist. Please make sure you're using the correct wallet ID | | 1306 | 400 | External Wallets | **Delete external wallet failed** Failed to delete the external wallet. Please make sure you've specified the correct external wallet parameters | | 1308 | 400 | External Wallets | **Create an external wallet failed** Failed to create an external wallet. Please verify you've provided the correct parameters for the creation as per the API docs | | 1309 | 400 | External Wallets | **Getting the wallet's asset failed** The external wallet based on the ID and asset specified does not exist. Please make sure you're using the correct asset and wallet ID | | 1312 | 400 | External Wallets | **Deleting specific asset from wallet failed** Please verify that you specified the correct wallet ID and asset ID for deletion | | 1313 | 400 | External Wallets | **Create an unsupported asset address in a wallet** The asset you're trying to add to the wallet is not supported. Verify the asset you're trying to use | | 1314 | 400 | External Wallets | **Create an asset from a different network in the wallet** The asset you're trying to add to the wallet is in a different network (mainnet or testnet), and your workspace does not support this network. Please verify the network of the asset and that your workspace correlates | | 1315 | 400 | External Wallets | **Add an asset in a wallet that already has it** You're trying to add an asset to the external wallet, but it already has this asset. Please make sure you're adding a new asset and not one that already exists | | 1316 | 400 | External Wallets | **Add an asset with an invalid address to the wallet** You're trying to add an asset to the external wallet, but the address does not match the asset's conventions. Please check the address you're trying to add | | | | | | | 1351 | 400 | Contract Wallets | **Create contract wallet failed** Contract wallet name must be specified, not empty, and to not already exist in a different wallet | | 1354 | 400 | Contract Wallets | **Get contract wallet by Id failed** The contract wallet requested does not exist. Please make sure you're using the correct wallet ID | | 1356 | 400 | Contract Wallets | **Delete contract wallet failed** Failed to delete the contract wallet. Please make sure you've specified the correct contract wallet parameters | | 1358 | 400 | Contract Wallets | **Create a contract wallet failed** Failed to create a contract wallet. Please verify you've provided the correct parameters for the creation as per the API docs | | 1359 | 400 | Contract Wallets | **Getting the wallet's asset failed** The contract wallet based on the ID and asset specified does not exist. Please make sure you're using the correct asset and wallet ID | | 1362 | 400 | Contract Wallets | **Deleting specific asset from wallet failed** Please verify that you specified the correct wallet ID and asset ID for deletion | | 1363 | 400 | Contract Wallets | **Create an unsupported asset address in a wallet** The asset you're trying to add to the wallet is not supported. Verify the asset you're trying to use | | 1364 | 400 | Contract Wallets | **Create an asset from a different network in the wallet** The asset you're trying to add to the wallet is in a different network (mainnet or testnet), and your workspace does not support this network. Please verify the network of the asset and that your workspace correlates | | 1365 | 400 | Contract Wallets | **Add an asset in a wallet that already has it** You're trying to add an asset to the contract wallet, but it already has this asset. Please make sure you're adding a new asset and not one that already exists | | 1366 | 400 | Contract Wallets | **Add an asset with an invalid address to the wallet** You're trying to add an asset to the contract wallet, but the address does not match the asset's conventions. Please check the address you're trying to add | | | | | | | 1401 | 400 | Transaction | **The operation requested is not supported** The operation argument that you provided in the create transaction is not supported. Please verify it's correct | | 1402 | 400 | Transaction | **Source balance insufficient** The source account does not have enough of the asset named in the error message to cover the transaction amount and network fee. The asset can be a base asset or a token | | 1403 | 400 | Transaction | **Destination balance is insufficient** Some blockchains (such as NEAR) require a minimal amount of assets to be activated. You tried to pass less than the minimal amount to a vault account that does not have such a wallet, thus the wallet can't be created. Please verify you're transferring a sufficient amount, or have the destination create a wallet and deposit a sufficient amount of base asset | | 1404 | 400 | Transaction | **Invalid Peer type** When creating the transaction, verify that the type of the source and destination are valid and one of the defined enums in the API docs | | 1405 | 404 | Transaction | **No such transaction** The transaction ID you used to try and get the transaction object is invalid and was not found in the system. Make sure you're using the correct transaction ID | | 1406 | 400 | Transaction | **Failed to cancel transaction** Cancelling the transaction failed for some reason. Please open a support ticket | | 1407 | 400 | Transaction | **User can't create a transaction** The user trying to create the transaction does not have sufficient permissions to do so. Please verify the user's permission level and that you're using the correct user | | 1408 | 400 | Transaction | **Paged get tried to change direction** When getting transactions in a paged fashion, you can only go in one direction (forward or backward) and can't change direction mid execution. Make sure you're only querying the information in one direction | | 1409 | 400 | Transaction | **Transaction creation parameters are invalid** Here is a list of items to check: 1. The asset type is valid and is supported 2. You can only specify `destination` or `destinations`, not both 3. For OTA, you must specify the address 4. Typed messages only supported BTC, LTC, DASH, and ETH 5. AML Customer reference ID can only use `-_:` and alpha-numeric 6. You can not have more than 650 destinations in the `destinations` parameters 7. Invalid `nodeControls` parameters | | 1410 | 400 | Transaction | **Transaction destination wallet does not exist** The destination is sending the transaction to an external/internal/contract wallet by an ID, but the ID specified does not exist. Check that the ID you're using is correct | | 1411 | 400 | Transaction | **Transaction destination wallet asset does not exist** The destination is sending the transaction to an external/internal/contract wallet by an ID, but the asset used in the transaction does not exist. Check that the asset has an address in the wallet ID | | 1412 | 400 | Transaction | **Transaction destination wallet address is suspended** The destination is an external/internal/contract wallet that has not been approved yet. Approve it and try again | | 1421 | 400 | Transaction | **Confirmation number change failed** Changing the number of confirmations for the transaction failed. Please verify that the transaction exists; otherwise, open a support ticket | | 1422 | 400 | Transaction | **Confirmation number change failed** Changing the number of confirmations for the transaction by transaction hash failed. Please make sure you're using the correct transaction hash; otherwise, open a support ticket | | 1423 | 400, 404 | Transaction | **Get transaction by transaction hash failed** Couldn't find a transaction with the specified transaction hash. Please verify the transaction hash and try again | | 1424 | 400 | Transaction, Network Connection | **Destination address invalid** Here is a list of parameters to check: 1. The address used is invalid for the asset you're using 2. The target (in case of a network transfer) does not contain a wallet for the specified asset 3. You're using the internal/external/contract wallet for a different workspace than the one you're performing the transaction on 4. You are trying to send a transaction to a network connection that you do not have 5. You're using `destinations` and one of the destinations is a network connection - this is not supported | | 1425 | 400 | Transaction | **Missing a tag** You're trying to send a transaction to an address that requires a tag. Please verify you're providing a tag | | 1426 | 400 | Transaction | **Unfreeze failed** You tried to unfreeze a transaction, but it was neither rejected nor failed by AML; therefore, it can't be unfrozen | | 1427 | 400 | Transaction | **Source type of transaction is invalid** You tried to send a transaction with the source being invalid. Verify that the source is one of: `VAULT_ACCOUNT`, `EXCHANGE_ACCOUNT`, `GAS_STATION` or `FIAT_ACCOUNT` | | 1428 | 400 | Transaction | **Destination of transaction is invalid** The destination type of one of the destinations is not one of the defined enums in the API docs. Verify that it is correct and try again | | 1429 | 400 | Asset, Transaction | **Validate address failed** The asset you tried to use for the `validate_address` endpoint is not supported | | 1431 | 400 | Asset, Transaction | **Validate address didn't find an address** The address provided to the `validate_address` endpoint is invalid. | | 1432 | 400 | Transaction | **Invalid amount specified** The amount must be a number or a numeric string; verify it is so | | 1433 | 400 | Transaction | **Invalid tag specified** The tag/memo specified does not comply with the rules of the blockchain. Verify the tag/memo is valid | | 1434 | 400 | Transaction | **Freeze failed** or **Cannot freeze** Depending on the context of the error code, you will see one of the above responses. Typically, freezing fails because of one of the following reasons: - The transaction is not in either the **Confirming** or the **Completed** status. - An input is not available. - There is an insufficient balance for freezing. - The transaction direction or asset type is not supported for freezing. | | 1435 | 400 | Transaction | **List unspent for unknown asset** You tried to get the unspent inputs (UTXO) for an asset that does not have such values, or no UTXOs exist | | 1437 | 400 | Exchange, Transaction | **Unsupported transfer** Direct transfers (Exchange to OTA) is not supported for the Kraken exchange | | 1438 | 400 | Transaction | **External Tx ID Duplicate** The `externalTxId` specified in the transaction was already used in the past. Please use a different one | | 1439 | 404 | Transaction | **No such External Tx ID** The external Tx ID used does not exist. Please verify the external Tx ID you're using | | 1440 | 400 | Transaction | **Drop for unsupported blockchain** You're trying to run a drop transaction request for a blockchain that is not `ETC` or `ETH` | | 1441 | 400 | Transaction | **Replace by fee too low** You're attempting to perform an RBF transaction, but the gas price you specified is too low. Please check it and submit a higher gas price | | 1443 | 400 | Transaction | **Too late for RBF** You're trying to replace by fee, but the transaction was already mined | | 1445 | 403 | Transaction | **User can't freeze** The user who is trying to perform a freeze does not have the permissions to perform this. Check the user's permissions and that you're using the correct user | | 1446 | 403 | Transaction | **User can't unfreeze** The user who is trying to perform an unfreeze does not have the permissions to perform this. Check the user's permissions and that you're using the correct user | | 1448 | 400 | Transaction | **Fee payer is not allowed for asset** You're trying to define a fee payer for an asset that does not support it. Please check whether the asset you're using allows for a fee payer configuration | | 1449 | 400 | Transaction | **Fee payer can't pay** The defined fee payer does not have a wallet with the base asset to pay the fee. Verify that you specified the correct fee payer and check that it contains a base asset wallet with a sufficient amount to pay for the transaction | | 1450 | 400 | Transaction | **Transactions are blocked for this blockchain** We have temporarily disabled transactions for the specified blockchain. Check the [status page](https://status.fireblocks.com) for more details | | 1451 | 400 | Transaction | **Transaction confirmations don't match Fireblocks restrictions** Fireblocks requires a specific number or a minimum number of confirmations for some blockchains. Manually setting confirmations for a transaction must meet these requirements. For more, see [Deposit Control & Confirmation Policy](https://support.fireblocks.io/hc/en-us/articles/29217252948892-Deposit-Control-and-Confirmation-Policy). | | 1454 | 400 | Transaction | **Missing license for creating a MEV protected transaction** This feature is not enabled in your workspace, and you should contact your Customer Success Manager or the Fireblocks Support team | | 1455 | 400 | Transaction, Gasless (meta-tx) | **Missing Gasless configuration** Gasless transactions aren’t set up for this workspace/asset. Configure Gasless (relayer/fee payer) or send a standard transaction. | | 1459 | 400 | Transaction | **Contract call operations require a base asset** You tried to create a contract call transaction with a token as the `assetId`. Contract calls only support a base asset (for example, `ETH`). Set `assetId` to the blockchain's base asset and try again | | 1502 | 400 | Asset, Transaction | **Trying to use a deprecated asset** The asset specified in the transaction is deprecated and can't be used. Please check the asset and use a different one | | 1503 | 400 | Asset, Balance, Transaction | **Unsupported asset** The asset specified is not supported. Please check the asset you've specified | | | | | | | 1603 | 400 | Network Connection | **Delete Network Connection but id is not specified** The delete network connection request does not have a `connectionId` parameter specified, please make sure one is provided and exists | | 1604 | 400 | Network Connection | **Create a network connection failed** The network connection creation failed. Check the error message for more information and open a support ticket if insufficient | | 1605 | 404 | Network Connection | **Get Network Connection for unknown id** The network connection ID provided could not be found. Please verify you're using the correct ID | | 1607 | 400 | Network Connection | **Setting the routing policy for a network connection failed** Verify that you used the proper parameters for the call | | 1608 | 404 | Network Connection | **No network connection with the specified ID is available to your workspace** Returned by all endpoints that accept a `connectionId`: get a network connection, delete a network connection, set a routing policy, and validate third-party routing. The ID may not exist, may belong to a different workspace, or may have been disabled. Verify the connection ID is correct and belongs to your workspace. | | 1609 | 400 | Network Connection | **Validating routing policy failed** The attempt to validate whether the network connection's routing will lead to a third party or not for a given asset failed. Please verify you passed the correct parameters and open a support ticket if needed | | 1611 | 403 | Network Connection | **No multiple network profiles** The workspace does not support multiple network profiles. Contact us to request support | | 1612 | 422 | Network Connection | **The counterparty rejected the connection request** The Counterparty has to send a connection request. | | 1613 | 422 | Network Connection | **The counterparty is already an active connection** The network connection already exists | | 1614 | 422 | Network Connection | **The connection is pending approval** You can request approval from the workspace admins | | 1615 | 422 | Network Connection | **The connection is pending approval** You can request approval from your counterparty | | 1616 | 403 | Network Connection | **Network connection cannot be deleted** There is an active Smart Transfers ticket with the connection, which must be closed before deleting the Network connection | | 9007 | 404 | Network Connection | **The Fireblocks Network is not activated in the workspace** Reach out to Fireblocks Support to resolve this | | 9011 | 403 | Network Connection | **Cannot delete network ID** The default ID cannot be deleted before making another ID the default | | 9012 | 403 | Network Connection | **Network profile cannot be deleted** It is the last remaining ID. There must be at least one Network profile per tenant | | 9013 | 403 | Network Connection | **Network profile cannot be deleted** There is an active Smart Transfers ticket with the Network profile, which must be closed before deleting the Network profile | | 9014 | 422 | Network Connection | **Invalid network name** The name can't contain the `<` and `>` characters | | 9015 | 403 | Network Connection | **Could not add an additional network ID** Contact Fireblocks Support to activate the feature | | 9016 | 422 | Network Connection | **The selected asset is not supported on the Fireblocks Network** Reach out to Fireblocks Support to resolve this | | 9017 | 422 | Network Connection | **Invalid routing policy > non-custom scheme** A routing destination type or ID is required | | 9018 | 422 | Network Connection | **Invalid routing policy > destination ID incompatible with selected asset.** Make sure you are sending a supported asset | | 9019 | 422 | Network Connection | **Invalid routing policy > selected routing policy is not allowed** Select a valid [routing policy](https://support.fireblocks.io/hc/en-us/articles/5671826748316-Flexible-Network-Routing) | | 9020 | 422 | Network Connection | **Invalid routing policy > selected routing destination is not supported** Select one of the [supported destinations](https://support.fireblocks.io/hc/en-us/articles/5671826748316-Flexible-Network-Routing) | | 9021 | 422 | Network Connection | **Invalid routing policy > selected crypto destination is not supported** Select a vault or a connected exchange account. | | 9022 | 422 | Network Connection | **Invalid routing policy > third-party account provided is invalid** Make sure you are adding a supported third-party destination | | 1706 | 400 | FIAT Accounts | **FIAT Account id provided is unknown** The exchange account ID you provided was not found. Please verify you're using the correct account ID | | 1708 | 400 | FIAT Accounts | **FIAT Account id provided is unknown** The exchange account ID you provided was not found. Please verify you're using the correct account ID | | | | | | | 1901 | 400 | Asset, Transaction | **Estimate fee for unknown asset** The asset you specified for the estimate fee call is unknown. Please make sure you're using a supported and known asset | | 1902 | 400 | Asset, Transaction | **Estimate fee for an invalid asset** The asset you specified does not support the estimate fee call. Please make sure you're using a supported and allowed asset | | 1904 | 400 | Asset, Transaction | **Estimate fee with invalid parameters** Make sure when calling the estimate fee endpoint that you do not have `destination` and `destinations` parameters both used, and that the `source` parameter is a vault account | | 1905 | 400 | Asset, Transaction | **Estimate fee failed due to insufficient funds** The funds you have in the source vault are not sufficient, and thus the estimate fee call failed. Make sure you have sufficient base asset in the source vault and try again | | 1907 | 400 | Asset, Transaction | **Estimate fee failed due to smart contract error.** The transaction reverted during its simulation as part of estimating the gas limit. There are three ways to properly handle this error: 1. Initiate the transaction again while using different parameters. 2. Try using a different vault to perform the transaction. 3. Read the contract’s source code to better understand the error. | | 1908 | 400 | Asset, Transaction | **Estimate fee failed since the amount you are sending is too small** The transaction failed since the amount you tried to send was not enough to pass our balance validation and internal tx checks. Confirm the transfer amount is above the required minimum for this blockchain or more than the transaction fees necessary before resubmitting the transaction. Minimum transfer amounts vary per blockchain. Known minimums: Bitcoin (BTC) - 546 satoshi Cardano (ADA) - 1 ADA + applicable transaction fees | | 1909 | 400 | Asset, Transaction | **Estimate failed due to insufficient gas fees** The transaction failed since you didn't have enough gas fees. Make sure you have sufficient gas money and try again. | | 2002 | 400 | Public Key | **Public key could not be found** Make sure that the information you provided to the public key query is valid, and a wallet for this asset exists in the specified vault account | | | | | | | 2101 | 400 | Gas Station | **Invalid gas station parameters** When editing the gas station configuration, make sure you provide both `gasCap` and `gasThreshold` | | 2110 | 400 | Gas Station | **Invalid asset for gas station** When editing the gas station configuration, the asset you specified is not supported for this operation. Make sure you're using a supported and valid asset | | | | | | | 2201 | 400 | Balance, Private Ledger | **Lock/Release allocation for unsupported asset** The asset you specified in the lock or release allocation call is not supported. Make sure it is a valid and supported asset | | 2202 | 400 | Balance, Private Ledger | **Lock/Release Allocation for unknown account** The virtual account you specified does not exist. Please make sure you're using a valid virtual account that exists | | 2204 | 400 | Private Ledger, Off-Exchange | **Allocation not allowed** The vault account you specified does not have off-exchange set up; therefore, locking allocation is not allowed. Make sure you have off-exchange set up and then try again | | 2205 | 400 | Private Ledger, Balance | **Insufficient amount for fee in allocation** The call you're trying to perform requires that a sufficient amount for the fee bank be allocated. The allocation amount you've specified does not meet this demand. Please increase the allocation amount | | | | | | | 2501 | 400 | Webhooks | **Resend webhook for specific tx id failed** Make sure one of the parameters `resendCreated` and `resendUpdated` is marked as true, and try again | | | | | | | 2602 | 404 | Off-Exchange | **Unknown virtual account id** The virtual account ID specified does not exist. Make sure you're using a virtual account ID that exists | | 2604 | 404 | Off-Exchange | **Unknown virtual account id** The virtual account ID specified does not exist. Make sure you're using a virtual account ID that exists | | | | | | | 2702 | 403 | Users | **Insufficient permissions for operation** The user who is trying to perform this call does not have sufficient permissions for it. Make sure you're using the correct user and that they have the proper permissions to view the user list | | | | | | | 2814 | 400 | Fee Payer | **Fee Payer not configured** No fee payer was configured for this workspace. Configure one and try again | | 2816 | 400 | Fee Payer | **Asset wallet does not exist** You've attempted to set a fee payer by specifying a vault account and asset; however, the specified asset does not have a wallet in the specified vault account. Please create a wallet in the specified vault account and fund it, then try again | | 2817 | 400 | Fee Payer | **Non-base asset** You've specified a fee payer asset that is not the base asset. This is not allowed. Please specify the base asset and try again | | 2818 | 400 | Fee Payer | **Unsupported asset** You've tried to set a fee payer for an asset that is not supported. Please verify you're using a supported asset and try again | | | | | | | 2902 | 403 | Audits | **Getting audits is not allowed** The user trying to get the audits is not allowed to due so due to a permissions matter. Make sure that the user has the proper permissions and that you're using the correct user | | | | | | | 3001 | 404 | Smart Transfers | **Ticket not found** Fireblocks could not find the Smart Transfer ticket. | | 3002 | 403 | Smart Transfers | **Ticket should be open** The attempted action can only be performed on an open Smart Transfer ticket. | | 3003 | 403 | Smart Transfers | **Ticket should be draft** The attempted action can only be performed on a Smart Transfer ticket draft. | | 3004 | 403 | Smart Transfers | **Ticket should be draft or open** The attempted action can only be performed on an open Smart Transfer ticket or a Smart Transfer ticket draft. | | 3005 | 403 | Smart Transfers | **Ticket action allowed for owner** Only the Smart Transfer ticket creator can perform the attempted action. | | 3006 | 403 | Smart Transfers | **Ticket cancel can be done by payer** You cannot close the Smart Transfer ticket because the connection has already funded it. | | 3007 | 403 | Smart Transfers | **Ticket terms are missing** You must define all required terms before you can open the Smart Transfer ticket. | | 3008 | 403 | Smart Transfers | **Ticket expiration date passed** The Smart Transfer ticket has expired. | | 3009 | 400 | Smart Transfers | **Term not found** No Smart Transfer ticket term matches your search criteria. | | 3010 | 403 | Smart Transfers | **Term should be created status** The submitted or updated Smart Transfer ticket term is incorrect. | | 3011 | 403 | Smart Transfers | **Term should be funded status** Only a funded Smart Transfer ticket can be closed. | | 3012 | 403 | Smart Transfers | **Term should be funding failed status** The attempt to fund the Smart Transfer ticket failed. | | 3013 | 403 | Smart Transfers | **Term amount required** You must enter a term amount before you can open a Smart Transfer ticket. | | 3014 | 403 | Smart Transfers | **Term asset required** You must select an asset before you can open a Smart Transfer ticket. | | 3015 | 403 | Smart Transfers | **Term asset not supported** Fireblocks does not support the selected asset for Smart Transfer tickets. | | 3016 | 403 | Smart Transfers | **Term from to should be different** The Smart Transfer ticket must include two different connections. | | 3017 | 403 | Smart Transfers | **Term amount is different than fund amount** The entered fund amount does not match the term amount on the Smart Transfer ticket. | | 3018 | 403 | Smart Transfers | **Term asset is different than fund asset** The asset selected for funding does not match the term's asset on the Smart Transfer ticket. | | 3019 | 403 | Smart Transfers | **Counterparty not found** Fireblocks could not find the counterparty connection. | | 3020 | 403 | Smart Transfers | **Network does not belong to tenant** You can only open a Smart Transfer ticket using a Fireblocks Network profile belonging to you. | | 3021 | 403 | Smart Transfers | **Networks not connected** The selected counterparty is not a Fireblocks Network connection. | | 3022 | 403 | Smart Transfers | **Term should be created or funding failed status** The Smart Transfer ticket has already been funded or is currently being funded. | | 3023 | 403 | Smart Transfers | **Term transaction can be made by counterparty of term who should pay** Your Fireblocks Network profile cannot fund the Smart Transfer ticket. | | 3024 | 403 | Smart Transfers | **Network connection does not belong to peers** The Fireblocks Network connection funding the Smart Transfer ticket does not match the connection listed on the term. | | 3025 | 403 | Smart Transfers | **Transaction fee and fee level defined** You cannot set a fee level (low, medium, or high) and specify a custom fee at the same time. | | 3027 | 403 | Smart Transfers | **Tickets can have at most two terms** Smart Transfer tickets can only have two transfer terms. | | 3029 | 403 | Smart Transfers | **Network doesn't have FF enabled** The Fireblocks Network connection does not use the Smart Transfer feature. | | 3030 | 400 | Smart Transfers | **Ticket missing expire in** You must set an expiration date before you can open a Smart Transfer ticket. | | 3031 | 403 | Smart Transfers | **Ticket creator must receive asset** You can only open a Smart Transfer ticket for receiving assets. | | 3101 | 404 | Trading | **Provider Not Found** Provider not found. | | 3102 | 400 | Trading | **Terms Approval Not Supported** This provider does not support terms of service approval. | | 3103 | 400 | Trading | **Same Chain Required** Input and output assets must be on the same chain. | | 3104 | 400 | Trading | **Cross Chain Required** Input and output assets must be on different chains. | | 3105 | 400 | Trading | **Unsupported Input Chain** The input blockchain is not supported by this provider. | | 3106 | 400 | Trading | **Unsupported Output Chain** The output blockchain is not supported by this provider. | | 3107 | 400 | Trading | **Unsupported Assets Pair** This trading pair is not supported by the provider. | | 3108 | 400 | Trading | **Settlement Required** This trade requires settlement information to be provided. | | 3109 | 400 | Trading | **Unsupported DVP Settlement Source/Destination** Only vault accounts can be used for DVP settlement type. | | 3110 | 400 | Trading | **Unsupported Settlement Pair** Source and destination must be the same vault account. | | 3111 | 400 | Trading | **Unsupported Side in DeFi Quote** DeFi trades only support selling (not buying) at this time. | | 3112 | 400 | Trading | **Unsupported Execution Type** This type of trade execution is not supported. | | 3113 | 400 | Trading | **Unsupported Asset Provider** This asset is not supported by this provider. | | 3114 | 400 | Trading | **Quote Not Available** No pricing quote is available for this trade at the moment. | | 3115 | 404 | Trading | **Quote Not Found** The price quote has expired or is no longer valid. | | 3116 | 404 | Trading | **Order Not Found** The trading order you requested does not exist. | | 3117 | 404 | Trading | **Source Account Not Found** Source account to extract funds not found for the given asset. | | 3118 | 404 | Trading | **Provider Account Not Found** The specified provider account was not found. | | 3119 | 400 | Trading | **Provider Account Not Active** The specified provider account is not active. | | 3120 | 403 | Trading | **Terms of Service Approval Required** You must approve the terms of service before trading with this provider. | | 3121 | 400 | Trading | **Quotes Unsupported** The provider does not support quote operation. | | 3122 | 403 | Trading | **Unmanaged Wallet Not Approved** Please approve the unmanaged wallet request before sending an order. | | 3123 | 400 | Trading | **Rates Unsupported** The provider does not support rate operation. | | 3124 | 400 | Trading | **Insufficient Funds** Insufficient balance. | | 3125 | 404 | Trading | **Destination Asset Wallet Not Found** Destination account to deposit funds not found for the given asset. | | 3126 | 400 | Trading | **Account Not Activated** Exchange/Connected account not activated. | | 3127 | 400 | Trading | **Blockchain Rail Unsupported for Fiat** A blockchain rail cannot be used with a fiat asset. | | 3300 | 500 | Staking | **Internal Error** An unexpected internal error occurred. Retry the request; if the issue persists, contact support. | | 3310 | 404 | Staking | **Position Not Found** The staking position you requested does not exist. Verify the position ID is correct. | | 3311 | 404 | Staking | **Validator Address Not Found** No validator address was found for the specified provider and chain. Verify the provider ID and chain descriptor. | | 3312 | 404 | Staking | **Provider Not Found** The staking provider you specified does not exist or is invalid. Verify the provider ID. | | 3313 | 404 | Staking | **Wallet Not Found** The specified wallet was not found. Verify the wallet address or vault account. | | 3320 | 409 | Staking | **Position State Invalid** The staking position is not in a valid state for the requested action (e.g. source or destination position is not active). Wait for any pending operations to complete before retrying. | | 3321 | 403 | Staking | **Terms of Service Approval Required** The provider's terms of service have not been approved. Approve the provider's terms of service before performing staking operations. | | 3322 | 409 | Staking | **Position Already Exists** A staking position already exists for this vault and provider combination (e.g. MATIC can only be staked once per provider per vault). Use the existing position or choose a different vault/provider. | | 3330 | 400 | Staking | **Chain Not Supported** The specified blockchain is not supported for staking. Use a supported chain descriptor. | | 3332 | 400 | Staking | **Action Unavailable** The requested action (e.g. unstake, withdraw, addToStake, consolidate) is not available for this position in its current state. Verify the position status before retrying. | | 3333 | 403 | Staking | **Not Available for Workspace** The requested staking feature or chain is disabled for your workspace (controlled via feature flags). Contact your account manager to enable it. | | 3341 | 400 | Staking | **Invalid Amount** The stake amount is invalid — it must be positive, non-zero, and meet chain-specific requirements (e.g. ETH must be at least 32 and divisible by 32; MATIC must be at least 1; Lido must be greater than 100 wei). Adjust the amount accordingly. | | 3381 | 409 | Staking | **Operation Already In Progress** Another staking operation is currently in progress on one of the involved positions. Wait for the existing operation to complete before initiating a new one. | | 9001 | 400 | Wallet | **Invalid customer reference id** The customer reference ID specified is invalid. Make sure it only consists of `\-\_:a-zA-Z0-9` | | 9003 | 400 | Transaction | **Invalid idempotency key length** The idempotency key must be no longer than 40 characters. Please verify that if you've specified one, it has a proper length **Idempotency key used on internal error request, returning bad request** This operation doesn't support the usage of the same idempotency key after an internal error | | 9004 | 400 | Vault | **An existing vault account by the same name already exists** Please try to create a different vault account using a different name. | | 9005 | 400 | Transaction | **A transaction with the same idempotency key is in progress** Please wait until the previous transaction for this idempotency key is done before issuing a new one | | 9006 | 400 | Network Connection | **Missing required parameter in request body** The request you've sent is missing the `isDiscoverable` parameter in the body, make sure it exists and is a boolean, then try again | | 9007 | 404 | Network Connection | **Unknown network id** The network ID specified could not be found. Please verify it exists and try again | | 9009 | 400 | Network Connection | **Parameters passed are invalid** The parameters passed to the `set_routing_policy` call are invalid, please make sure they are correct and conform to the API docs, then try again | | 11001 | 400 | All | **The specified vault account is invalid** Please make sure that the vault account you've specified is correct, valid, and exists | | 12000 | 400 | All | **Cloud provider issue** There is an issue with one of our cloud providers. Please check the [status page](https://status.fireblocks.com) for more details | ## Reports API error codes The Reports API returns errors in the following envelope: ```json theme={"system"} { "errorCode": , "name": "", "message": "" } ``` Validation responses (`400` `12001`) also include a `details` array. `5XX` responses carry no `errorCode`. > [!NOTE] > > These `errorCode` values are scoped to the Reports API and are distinct from the global codes in the table above. For example, `12000` here is `INVALID_REPORT_TYPE`, not the global **Cloud provider issue** error. | Error Code | HTTP Status Code | Name | Endpoint(s) | Meaning | | - | - | - | - | - | | 12000 | 400 | `INVALID_REPORT_TYPE` | `POST /v1/reports` | `reportType` is not a supported value. | | 12001 | 400 | `INVALID_FILTERS` | `POST /v1/reports` | `filters` don't match the schema for the chosen `reportType` (includes oversized filter arrays and keys not on the schema). | | 12002 | 400 | `INVALID_OUTPUT_FORMAT` | `POST /v1/reports` | `outputFormat` is not a supported value (only `CSV` is supported). | | 12005 | 409 | `REPORT_ALREADY_IN_FLIGHT` | `POST /v1/reports` | An identical report (same filters) is already running; the response body returns the existing `id`. | | 12006 | 401 | `UNAUTHORIZED` | all | Authentication token (JWT) is missing or invalid. | | 12007 | 403 | `FORBIDDEN` | all | Reports are not enabled for this tenant. | | 12008 | 404 | `REPORT_NOT_FOUND` | `GET /v1/reports/{id}` | No report exists with that id (a report owned by another tenant is also reported as not-found). | | 12009 | 429 | `RATE_LIMIT_EXCEEDED` | `POST /v1/reports` | Rate limit exceeded; a `Retry-After` header is included. | ## 5XX Error Codes ### 10XX codes | Error Code | Feature | Description | | - | - | - | | 1001 | Vault | Get Vault accounts paged failed | | 1003 | Vault | Create vault account failed | | 1005 | Vault | Get vault account failed | | 1007 | Asset, Balance | Get balance for vault account and asset failed | | 1009 | Wallet, Asset | Create wallet for asset failed | | 1011 | Wallet | Adding a new address failed | | 1012 | Wallet | Getting the addresses for the given asset failed | | 1014 | Vault | Renaming a vault account failed | | 1018 | Vault | Hide vault account failed | | 1021 | Vault | Unhide vault account failed | | 1029, 1030 | Asset, Balance | Get max spendable amount failed | | 1033 | Asset | Get balance for assets or specific asset failed | | 1036 | Asset | Fetching max BIP44 index used failed | ### 11XX codes | Error Code | Feature | Description | | - | - | - | | 1101 | Exchange, Fiat | Failed to get exchange/fiat accounts | | 1102 | Exchange | Failed to get exchange account by ID | | 1104 | Exchange | Failed to get exchange account by ID and asset | | 1107 | Exchange Transfers | Failed to perform an exchange account transfer (internal transfer) | | 1110 | Exchange Transfers | Convert operation failed | ### 12XX codes | Error Code | Feature | Description | | - | - | - | | 1201 | Internal Wallets | Failed to get internal wallets | | 1202 | Internal Wallets | Failed to create an internal wallet | | 1203 | Internal Wallets | Failed to get internal wallet by ID | | 1205 | Internal Wallets | Failed to delete internal wallet | | 1207 | Internal Wallets | Failed to create an internal wallet by asset | | 1210 | Internal Wallets | Failed to get wallet by asset and wallet ID | | 1211 | Internal Wallets | Failed to delete specific asset from wallet | ### 13XX codes | Error Code | Feature | Description | | - | - | - | | 1301 | External Wallets | Failed to get external wallets | | 1302 | External Wallets | Failed to create an external wallet | | 1303 | External Wallets | Failed to get external wallet by ID | | 1305 | External Wallets | Failed to delete an external wallet | | 1307 | External Wallets | Failed to create an external wallet by asset | | 1310 | External Wallets | Failed to get wallet by asset and wallet ID | | 1311 | External Wallets | Failed to delete specific asset from wallet | | 1350 | External Wallets | Failed to get external wallets | | 1352 | External Wallets | Failed to create an external wallet | | 1353 | External Wallets | Failed to get external wallet by ID | | 1355 | External Wallets | Failed to delete external wallet | | 1357 | External Wallets | Failed to create an external wallet by asset | | 1360 | External Wallets | Failed to get wallet by asset and wallet ID | | 1361 | External Wallets | failed to delete specific asset from wallet | ### 14XX codes | Error Code | Feature | Description | | - | - | - | | 1404 | Transaction | Transaction creation or get failed | | 1430 | Asset, Transaction | Validate destination address endpoint failed | | 1436 | Transaction | List unspent inputs (UTXO) failed | | 1444 | Transaction | Failed to get the transactions list | | 1447 | Transaction | Failed to get transaction by external Tx ID | ### 15XX codes | Error Code | Feature | Description | | - | - | - | | 1501 | Asset | Get supported assets failed | ### 17XX codes | Error Code | Feature | Description | | - | - | - | | 1702 | Fiat Accounts | Get fiat account by ID failed | | 1707 | Fiat Accounts | Redeem funds to DDA failed | | 1709 | Fiat Accounts | Deposit funds from DDA failed | ### 19XX codes | Error Code | Feature | Description | | - | - | - | | 1903 | Asset, Transaction | Estimate fee calculation failed | ### 20XX codes | Error Code | Feature | Description | | - | - | - | | 2001 | Public Key | Getting public key information failed | ### 21XX codes | Error Code | Feature | Description | | - | - | - | | 2102 | Fireblocks Gas Station | Setting gas station information failed | ### 22XX codes | Error Code | Feature | Description | | - | - | - | | 2203 | Balance, Private Ledger | Lock allocation call failed | ### 25XX codes | Error Code | Feature | Description | | - | - | - | | 2501 | Webhook | Resend webhook failed | ### 26XX codes | Error Code | Feature | Description | | - | - | - | | 2601 | Off-Exchange | Get off-exchange failed | | 2603 | Off-Exchange | Couldn't settle off-exchange with provided virtual account ID | | 2605 | Off-Exchange | Failed to get off-exchange with specified ID | ### 27XX codes | Error Code | Feature | Description | | - | - | - | | 2701 | Users | Failed to get users for workspace | ### 28XX codes | Error Code | Feature | Description | | - | - | - | | 2813 | Fee Payer | Failed to get fee payer | | 2815 | Fee Payer | Setting the fee payer failed | | 2819 | Fee Payer | Deleting the fee payer configuration failed | ### 29XX codes | Error Code | Feature | Description | | - | - | - | | 2901 | Audits | Failed to get audits | ### 31XX codes | Error Code | Feature | Description | | - | - | - | | 3180 | Trading | An unexpected error occurred. Please try again. | | 3181 | Trading | Failed to create quote. Please try again. | # API Idempotency Source: https://developers.fireblocks.com/reference/api-idempotency The Fireblocks API supports idempotent requests (multiple requests which have the same result as making a single request). This is helpful when you don’t get a response or the API call isn’t completed successfully. ## Idempotency key > [!WARNING] > > Do not use `Idempotency-Key` when you create transactions. Use the `externalTxId` parameter instead, as described in [Transaction idempotency](#transaction-idempotency). > [!NOTE] > > * You can use idempotency keys with **POST** and **PUT** requests. Using `Idempotency-Key` in your request header for **GET** and **DELETE** will not work as they are inherently idempotent. > * The `` value in your idempotent request can have a maximum of 40 characters. To submit an idempotent request, add `Idempotency-Key: ` to the header of your POST or PUT request. When you provide the same idempotency key for two requests, we always return the previously saved response of the first API request, even if it was an error. Fireblocks does not re-run the operation. This ensures a request with the same idempotency key is not executed multiple times if you re-submit it due to network errors or delays from a prior submission. Fireblocks stores the response from the first request, including error responses, and returns it for every subsequent request with the same idempotency key for 24 hours. After 24 hours, generate a new key. ## Transaction Idempotency When creating transactions, use the `externalTxId` parameter rather than an idempotency key in the [Create Transaction API](/transactions/create-transactions) call. This parameter ensures that the transaction is idempotent. If another transaction request is made with the same `externalTxId` value, it will be rejected with an HTTP 400 code and an error message. Learn more about the `externalTxId` parameter [here](/docs/manage-withdrawals-at-scale#idempotent-transactions) # API Overview Source: https://developers.fireblocks.com/reference/api-overview ## REST API Fireblocks offers a robust REST API that enables developers to leverage Fireblocks' capabilities programmatically. Our REST API serves as the foundation for all Fireblocks SDKs. To browse and call endpoints directly, see the [API Endpoints Overview](/reference/api-endpoints-overview). To practice with our API before implementing any code, read the [Postman Guide](/reference/explore-postman-collection) containing the pre-defined API endpoints. ### OpenAPI specification [View the Fireblocks OpenAPI 3.0 specification](https://swagger.fireblocks.com/openapi.yaml). Fireblocks uses the OpenAPI 3.0 (Swagger) format to maintain our API and provide the specification file for download. The API Reference section is auto-generated from our OpenAPI specification. You can also import it into your tool of choice, such as Swagger UI, ReDoc, and so on. ### Generate client libraries and SDKs Using the Swagger Editor, you can generate your client libraries and SDKs in the language of your choice. 1. [Navigate to the publicly hosted Swagger Editor](https://editor.swagger.io/). 2. Copy [our latest OpenAPI specification file](https://swagger.fireblocks.com/openapi.yaml) and paste it into the editor window. 3. Select **Generate Client** on the top menu bar and select the language to download. 4. Set up the proper headers to authenticate with the Fireblocks API. See [JWT structure](/reference/signing-a-request-jwt-structure#jwt-structure) for details. You can review how our SDKs sign requests and replicate the same approach in your generated client library. ### Fireblocks Developers Hub Visit our [Developers Hub GitHub repository](https://github.com/fireblocks/developers-hub) for code examples, snippets, and other helpful resources that you can use as a reference. ## Language-specific SDKs & guides Fireblocks supports and maintains SDKs in JavaScript and Python to help you interact with the Fireblocks API. We also offer guides for languages in which we don't have an active SDK. These guides provide you with a simple example to get you started and quickly pass the first hurdles of securely signing API requests: * [Typescript SDK](/reference/typescript-sdk) * [Python SDK](/reference/new-python-sdk) * [Java SDK](/reference/java-sdk) ## Need help deciding which language to use? Use the following decision tree to help you decide which Fireblocks SDK is best for your use case. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/005f1df155944ae50f4ee082770b112545e458ab05a94a49450ee3636dc51f33-sdk_flow.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=b4d77d474c8de8c5f7287c28ae462b33) # Web3/Smart Contract Fireblocks offers Web3 connector SDKs for developers who use a base library as part of their tech stack and want Fireblocks to act as the underlying wallet and security layer: * [EVM Web3 Provider](/reference/evm-web3-provider): Learn about using `ethers.js`, `web3.js` or `web3.py` with Fireblocks as the Web3 Provider. # Additional Tools * [Fireblocks Hardhat Plugin](https://github.com/fireblocks/hardhat-fireblocks) - An easy-to-use plugin to enable Fireblocks signing for smart contract deployment using Hardhat. More information at [Fireblocks Hardhat plugin guide](/reference/hardhat-plugin). * [Fireblocks Local JSON RPC](https://github.com/fireblocks/fireblocks-json-rpc) - A locally running EVM JSON RPC module that uses the Web3 Provider to use Fireblocks as the signing mechanism. This allows you to plug Fireblocks into any of the tools that require a JSON RPC URL, including different development and deployment tools. More information at [Fireblocks Local JSON RPC Guide](/reference/evm-local-json-rpc). # Webhooks Webhook notifications allow you to get push notifications for events that happen in your Fireblocks workspace directly to an HTTP webhook URL of your choice. This saves you the need to constantly check the API for updates. Read more at [Configure Webhooks](/reference/configure-webhook-urls). # Audit Log Events Source: https://developers.fireblocks.com/reference/audit-log-events This page includes all Audit Log events and associated parameters. Sections are organized by Category. # Administration | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.ApiIpWhitelisting_ListUpdated` | Whitelisted IP | List updated | API IP Whitelisting List Updated | | `EventId.AdminQuorum_ApprovedByAdmin` | Quorum | Approved | Admin Quorum Approved by admin | | `EventId.AdminQuorum_Cancelled` | Quorum | Canceled | Admin Quorum Cancelled | | `EventId.AdminQuorum_Rejected` | Quorum | Rejected | Admin Quorum Rejected | | `EventId.AdminQuorum_RequestSubmitted` | Quorum | Submitted request | Admin Quorum Request Submitted | | `EventId.AdminQuorum_ThresholdChanged` | Quorum | Changed threshold | Admin Quorum Threshold Changed | | `EventId.Authentication_LoginFailed` | Sign in | Failed | Authentication Login Failed | | `EventId.Authentication_PasswordChangeRequested` | Sign in | Requested password change | Authentication Password Change Requested | | `EventId.Authentication_PasswordChanged` | Sign in | Changed password | Authentication Password Changed | | `EventId.Authentication_2faReset` | Sign in | Reset 2FA | Authentication 2FA reset | | `EventId.DeviceRecover_Completed` | Device recovery | Completed | Device Recover Completed | | `EventId.DeviceReset_ApprovedByAdmin` | Device reset | Approved | Device Reset Approved by admin | | `EventId.DeviceReset_DeviceRecover` | Device reset | Recovered | Device Reset Device Recover | | `EventId.DeviceReset_DeviceReset` | Device reset | Completed | Device Reset Device Reset | | `EventId.DeviceReset_Rejected` | Device reset | Rejected | Device Reset Rejected | | `EventId.DeviceReset_RequestSubmitted` | Device reset | Submitted request | Device Reset Request submitted | | `EventId.EditUser_ApprovedByAdmin` | Edit user | Approved | Edit User Approved by admin | | `EventId.EditUser_Cancelled` | Edit user | Canceled | Edit User Cancelled | | `EventId.EditUser_EditRequestSubmitted` | Edit user | Submitted request | Edit User Edit request submitted | | `EventId.EditUser_EditRequestImplemented` | Edit user | Edit request implemented | | | `EventId.EditUser_Rejected` | Edit user | Rejected | Edit User Rejected | | `EventId.NewUserApproval_ApprovedByAdmin` | New user | Approved | New User Approval Approved by admin | | `EventId.NewUserApproval_Cancelled` | New user | Canceled | New User Approval Cancelled | | `EventId.NewUserApproval_Rejected` | New user | Rejected | New User Approval Rejected | | `EventId.NewUserApproval_RequestSubmitted` | New user | Submitted request | New User Approval Request submitted | | `EventId.OneTimeAddress_ApprovedByAdmin` | One-time address | Approved | One-time Address Approved by admin | | `EventId.OneTimeAddress_Cancelled` | One-time address | Canceled | One-time Address Cancelled | | `EventId.OneTimeAddress_Rejected` | One-time address | Rejected | One-time Address Rejected | | `EventId.OneTimeAddress_RequestSubmitted` | One-time address | Submitted request | One-time Address Request Submitted | | `EventId.OneTimeAddress_ToggledOff` | One-time address | Turned off | One-time Address Toggled off | | `EventId.OneTimeAddress_ToggledOn` | One-time address | Turned on | One-time Address Toggled on | | `EventId.ReEnrollDevice_ApprovedByAdmin` | Re-enroll mobile device | Approved | Re-enroll Device Approved by admin | | `EventId.ReEnrollDevice_Cancelled` | Re-enroll mobile device | Canceled | Re-enroll Device Cancelled | | `EventId.ReEnrollDevice_Completed` | Re-enroll mobile device | Completed | Re-enroll Device Completed | | `EventId.ReEnrollDevice_DeviceReEnrollmentCompleted` | Re-enroll mobile device | Completed | Re-enroll Device Device Re-enrollment Completed | | `EventId.ReEnrollDevice_DeviceRecover` | Re-enroll mobile device | Recovered | Re-enroll Device Device Recover | | `EventId.ReEnrollDevice_Rejected` | Re-enroll mobile device | Rejected | Re-enroll Device Rejected | | `EventId.ReEnrollDevice_RequestSubmitted` | Re-enroll mobile device | Submitted request | Re-enroll Device Request Submitted | | `EventId.User_Deleted` | User | Deleted | User Deleted | | `EventId.User_LoggedIn` | User | Signed in | User Logged In | | `EventId.User_AccessDenied` | User | Access denied | User Access denied | | `EventId.UserGroups_ChangesApprovedByAdmin` | User group | Changes approved | User Groups Active group changes request was approved by admin | | `EventId.UserGroups_ChangesRequestRejected` | User group | Changes rejected | User Groups Active group changes request was rejected | | `EventId.UserGroups_ChangesWentIntoAffect` | User group | Changes in effect | User Groups Active group changes went into affect | | `EventId.UserGroups_EditRequestImplemented` | User group | Edited | User Groups Edit request implemented | | `EventId.UserGroups_NewGroupApprovedAndCreated` | User group | Approved and created | User Groups New group approved and created | | `EventId.UserGroups_NewGroupRequestWasApprovedByAdmin` | User group | Approved | User Groups New group request was approved by admin | | `EventId.UserGroups_NewGroupRequestWasRejected` | User group | Rejected | User Groups New group request was rejected | | `EventId.UserGroups_UserCanceledActiveGroupChangesRequest` | User group | Canceled change request | User Groups User canceled active group changes request | | `EventId.UserGroups_UserDeletedAGroup` | User group | Deleted | User Groups User deleted a group | | `EventId.UserGroups_UserDiscardedActiveGroupEditing` | User group | Discarded edits (DEPRECATED) | User Groups User discarded active group editing | | `EventId.UserGroups_UserStartedEditingActiveGroup` | User group | Edited (DEPRECATED) | User Groups User started editing active group | | `EventId.UserGroups_SubmittedApprovalRequestGroupChanges` | User group | Requested change approval | User Groups User submitted approval request for active group changes | | `EventId.UserGroups_UserSubmittedNewGroupForApproval` | User group | Created | User Groups User submitted new group for approval | | `EventId.UserManagement_DeviceRecover` | User | Recovered mobile device | User Management Device Recover | | `EventId.UserManagement_DeviceReset` | User | Reset mobile device | User Management Device Reset | | `EventId.UserManagement_Disabled` | User | Deactivated | User Management Disabled | | `EventId.UserManagement_RoleChanged` | User | Changed role | User Management Role Changed | | `EventId.LinkedUserMigration_InitiatedByUser` | Linked user migration | Initiated by user | Linked user migration Initiated by user | | `EventId.LinkedUserMigration_UserPairedSuccessfully` | Linked user migration | User paired successfully | Linked user migration User paired successfully | | `EventId.LinkedUserMigration_Activated` | Linked user migration | Activated | Linked user migration Activated | | `EventId.LinkedUserMigration_Deactivated` | Linked user migration | Deactivated | Linked user migration Deactivated | | `EventId.Notifications_EmailCreated` | Email Notification | Created | | | `EventId.Notifications_EmailEnabled` | Email Notification | Enabled | | | `EventId.Notifications_EmailDisabled` | Email Notification | Disabled | | | `EventId.Notifications_EmailEdited` | Email Notification | Edited | | | `EventId.Notifications_SlackCreated` | Slack Notification | Created | | | `EventId.Notifications_SlackEnabled` | Slack Notification | Enabled | | | `EventId.Notifications_SlackDisabled` | Slack Notification | Disabled | | | `EventId.Notifications_SlackEdited` | Slack Notification | Edited | | | `EventId.Notifications_WebhooksCreated` | Webhooks Notification | Created | | | `EventId.Notifications_WebhooksEnabled` | Webhooks Notification | Enabled | | | `EventId.Notifications_WebhooksDisabled` | Webhooks Notification | Disabled | | | `EventId.Notifications_WebhooksEdited` | Webhooks Notification | Edited | | | `EventId.Notifications_WebhooksDeleted` | Webhooks Notification | Deleted | | # Assets | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.Asset_Listed` | Asset | Listed | Asset Listed | | `EventId.Asset_SetPrice` | Asset | Set price | Asset Set price | # Automation | Event ID | Event | Subject | Notification Subject | | - | - | - | - | | `EventId.AutomationRule_RuleCreated` | Rule Created | Automation Rule | Automation Rule Rule Created | | `EventId.AutomationRule_RuleEdited` | Rule Edited | Automation Rule | Automation Rule Rule Edited | | `EventId.AutomationRule_RuleEnabled` | Rule Enabled | Automation Rule | Automation Rule Rule Enabled | | `EventId.AutomationRule_RuleDisabled` | Rule Disabled | Automation Rule | Automation Rule Rule Disabled | | `EventId.AutomationRule_RuleDeleted` | Rule Deleted | Automation Rule | Automation Rule Rule Deleted | # Compliance | Event ID | Subject | Event Description | Notification Subject | | - | - | - | - | | `EventId.Transaction_AmlRegistrationComplated` | Compliance | AML registration completed | Transaction AML Registration complated | | `EventId.Transaction_AmlRegistrationStarted` | Compliance | AML registration initiated | Transaction AML Registration started | | `EventId.Transaction_AmlScreeningCompleted` | Compliance | AML screening completed | Transaction AML screening completed | | `EventId.Transaction_AmlScreeningFailed` | Compliance | AML screening failed | Transaction AML screening failed | | `EventId.Transaction_AmlScreeningInBackground` | Compliance | AML screening in background | Transaction AML screening in background | | `EventId.Transaction_AmlScreeningStarted` | Compliance | AML screening initiated | Transaction AML screening started | | `EventId.Transaction_ScreeningCompleted` | Compliance | Screening completed | Transaction Screening completed | | `EventId.Transaction_ScreeningStarted` | Compliance | Screening initiated | Transaction Screening Started | | `EventId.Transaction_ScreeningUpdateCompleted` | Compliance | Screening update completed | Transaction Screening update completed | | `EventId.Transaction_TravelRuleScreeningCompleted` | Compliance | Travel rule screening completed | Transaction Travel Rule screening completed | | `EventId.Transaction_TravelRuleScreeningFailed` | Compliance | Travel rule screening failed | Transaction Travel Rule screening failed' | | `EventId.Transaction_TravelRuleScreeningStarted` | Compliance | Travel rule screening initiated | Transaction Travel Rule screening started', 'Travel Rule screening started | | `EventId.Transaction_TravelRuleScreeningUpdateCompleted` | Compliance | Travel rule screening update completed | Transaction Travel Rule screening update completed | | `EventId.Transaction_TravelRuleScreeningUpdateStarted` | Compliance | Travel rule screening update initiated | Transaction Travel Rule screening update started | # Developers | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `WebhookUrls_ListUpdated` | Webhooks | Updated URLs | Webhook URLs List Updated | | `Webhook_Added` | Webhook endpoint | Added | | | `Webhook_Deleted` | Webhook endpoint | Deleted | | | `Webhook_Activated` | Webhook endpoint | Activated | | | `Webhook_Deactivated` | Webhook endpoint | Deactivated | | | `Webhook_Updated` | Webhook endpoint | Updated | | | `Webhook_Suspended` | Webhook endpoint | Suspended | | | `Webhook_Suspended_Warning` | Webhook endpoint | Suspend warning sent | | # Exchanges | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.ExchangeConnection_Added` | Exchange account | Linked | Exchange Connection Added | | `EventId.ExchangeConnection_Removed` | Exchange account | Unlinked | Exchange Connection Removed | | `EventId.ExchangeConnectionRequest_ApprovedByAdmin` | Exchange account | Approved | Exchange Connection Request Approved by admin | | `EventId.ExchangeConnectionRequest_Cancelled` | Exchange account | Canceled | Exchange Connection Request Cancelled | | `EventId.ExchangeConnectionRequest_Rejected` | Exchange account | Rejected | Exchange Connection Request Rejected | | `EventId.ExchangeConnectionRequest_Submitted` | Exchange account | Submitted | Exchange Connection Request Submitted | | `EventId.FiatConnectionRequest_ApprovedByAdmin` | Fiat account | Approved | FIAT Connection Request Approved by admin | | `EventId.FiatConnectionRequest_Rejected` | Fiat account | Rejected | FIAT Connection Request Rejected | | `EventId.FiatConnectionRequest_Submitted` | Fiat account | Submitted | FIAT Connection Request Submitted | | `EventId.FiatConnectionRequest_Cancelled` | Fiat account | Canceled | FIAT Connection Request Cancelled | | `EventId.FiatConnection_Added` | Fiat account | Linked | FIAT connection Added | | `EventId.FiatConnection_Removed` | Fiat account | Unlinked | FIAT connection Removed | # Keys | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | ValidationKey\_SubmittedRequest | Validation key | Submitted request | Validation key Submitted request | | ValidationKey\_Approved | Validation key | Approved | Validation key Approved | | ValidationKey\_Rejected | Validation key | Rejected | Validation key Rejected | | ValidationKey\_Activated | Validation key | Activated | Validation key Activated | | ValidationKey\_Deactivated | Validation key | Deactivated | Validation key Deactivated | | SigningKey\_SubmittedRequest | Signing key | Submitted request | Signing key Submitted request | | SigningKey\_LinkedToUser | Signing key | Linked to user | Signing key Linked to user | | SigningKey\_Enabled | Signing key | Enabled | Signing key Enabled | | SigningKey\_AssignedToVaultAccount | Signing key | Assigned to vault account | Signing key Assigned to vault account | | SigningKey\_Deleted | Signing key | Deleted | Signing key Deleted | | MPCKeySet\_Created | MPC key set | Created | | | MPCKeySet\_Enabled | MPC key set | Enabled | | | MPCKeySet\_Activated | MPC key set | Activated | | # Policies | Event subject | Event type | | - | - | | Policy states | Rejected policy | | Policy states | Signed policy | | Policy states | Deleted policy | | Policy states | Published policy | | Policy states | Discarded policy draft | | Policy states | Reverted policy draft | | Policy states | Put policy into effect | | Policy states | Draft marked as ready to publish | | Policy states | Requested policy draft publishing | | Policy states | Edited draft | | IP address allowlist activation | Submitted | | IP address allowlist activation | Approved by quorum | | IP address allowlist activation | Approved by user | | IP address allowlist activation | Rejected | | IP address allowlist activation | Canceled | | IP address allowlist activation | Failed | | IP address allowlist deactivation | Submitted | | IP address allowlist deactivation | Approved by quorum | | IP address allowlist deactivation | Approved by user | | IP address allowlist deactivation | Rejected | | IP address allowlist deactivation | Canceled | | IP address allowlist deactivation | Failed | | IP address allowlist | Approval submitted | | IP address allowlist | Approval approved by quorum | | IP address allowlist | Approval approved by user | | IP address allowlist | Approval canceled | | IP address allowlist | Approval rejected | | IP address allowlist | Approval failed | | IP address allowlist | Changes submitted | | IP address allowlist | Changes approved by quorum | | IP address allowlist | Changes approved by user | | IP address allowlist | Changes canceled | | IP address allowlist | Changes rejected | | IP address allowlist | Changes failed | | IP address allowlist | Deletion submitted | | IP address allowlist | Deletion approved by user | | IP address allowlist | Deletion canceled | | IP address allowlist | Deletion approved by quorum | | IP address allowlist | Deletion rejected | | IP address allowlist | Deletion failed | # Orders | Event subject | Event type | | - | - | | Order | Order created | # Security | Event ID | Subject | Event | | :- | :- | :- | | EventId.Fspm\_FindingCreated | FSPM | Finding created | | EventId.Fspm\_FindingResolved | FSPM | Finding resolved | | EventId.Fspm\_FindingAccepted | FSPM | Finding accepted | | EventId.Fspm\_FindingReopened | FSPM | Finding reopened | # Settlements | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.NetworkConnection_Added` | Fireblocks network | Added connection | Network Connection Added | | `EventId.NetworkConnection_Created` | Fireblocks network | Made connection | Network Connection Created | | `EventId.NetworkConnectionInvitation_ApprovedByAdmin` | Fireblocks network | Approved invitation | Network Connection Invitation Approved by admin | | `EventId.NetworkConnectionInvitation_Cancelled` | Fireblocks network | Canceled invitation | Network Connection Invitation Cancelled | | `EventId.NetworkConnectionInvitation_ReceivedFromCounterparty` | Fireblocks network | Received invitation | Network Connection Invitation Received from Counterparty | | `EventId.NetworkConnectionRequest_ApprovedByAdmin` | Fireblocks network | Invitation approved by connection | Network Connection Request Approved by admin | | `EventId.NetworkConnectionRequest_Cancelled` | Fireblocks network | Invitation canceled by connection | Network Connection Request Cancelled | | `EventId.NetworkConnectionRequest_Rejected` | Fireblocks network | Invitation rejected by connection | Network Connection Request Rejected | | `EventId.NetworkConnectionRoutingChange_Approved` | Fireblocks network | Approved routing change | Network Connection Routing Change Approved | | `EventId.NetworkConnectionRoutingChange_ApprovedByAdmin` | Fireblocks network | Approved routing change (admin) | Network Connection Routing Change Approved By Admin | | `EventId.NetworkConnectionRoutingChange_Rejected` | Fireblocks network | Rejected routing change | Network Connection Routing Change Rejected | | `EventId.NetworkConnectionRoutingChange_RequestSubmitted` | Fireblocks network | Requested routing change | Network Connection Routing Change Request Submitted | | `EventId.NetworkConnectionRoutingChange_RoutingChanged` | Fireblocks network | Changed routing | Network Connection Routing Change Routing Changed | | `EventId.NetworkDiscoverability_TurnedOn` | Fireblocks network | Turned on discoverability' | Network Discoverability Turned On | | `EventId.NetworkDiscoverability_TurnedOff` | Fireblocks network | Turned off discoverability' | Network Discoverability Turned Off | | `EventId.NetworkProfile_Created` | Fireblocks network | Created profile' | Network Profile Created | | `EventId.NetworkProfileNameChange_ProfileRenamed` | Fireblocks network | Approved profile name change | Network Profile Name Change Profile Renamed | | `EventId.NetworkProfileNameChange_RequestSubmitted` | Fireblocks network | Requested profile name change | Network Profile Name Change Request Submitted | | `EventId.NetworkProfileRemoved_ProfileRemoved` | Fireblocks network | Removed profile | Network Profile Removed Profile Removed | | `EventId.NetworkProfileRoutingChange_Approved` | Fireblocks network | Approved default routing change | Network Profile Routing Change Approved | | `EventId.NetworkProfileRoutingChange_ApprovedByAdmin` | Fireblocks network | Approved default routing change' | Network Profile Routing Change Approved By Admin | | `EventId.NetworkProfileRoutingChange_Rejected` | Fireblocks network | Rejected default routing change | Network Profile Routing Change Rejected | | `EventId.NetworkProfileRoutingChange_RequestSubmitted` | Fireblocks network | Requested default routing change | Network Profile Routing Change Request Submitted | | `EventId.NetworkProfileRoutingChange_RoutingChanged` | Fireblocks network | Changed default routing | Network Profile Routing Change Routing Changed | | `EventId.NetworkProfileRoutingChange_WaitingForApproval` | Fireblocks network | Pending approval | Network Profile Routing Change Waiting For Approval | | `EventId.NetworkConnection_Removed` | Fireblocks network | Removed connection | Network Connection Removed | | `EventId.NetworkConnection_RemovedByCounterparty` | Fireblocks network | Removed by connection | Network Connection Removed by counterparty | | `EventId.NetworkConnectionInvitation_Rejected` | Fireblocks network | Rejected invitation | Network connection invitation Rejected | | `EventId.NetworkConnectionRequest_Submitted` | Fireblocks network | Sent invitation | Network connection request Submitted | # Transactions | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.IncomingTransaction_AssociationFailed` | Incoming transaction | Failed | Incoming Transaction Association Failed | | `EventId.IncomingTransaction_Completed` | Incoming transaction | Completed | Incoming Transaction Completed | | `EventId.IncomingTransaction_Submitted` | Incoming transaction | Submitted | Incoming Transaction Submitted | | `EventId.IncomingTxAssociationFailed_FailedToAssociateIncomingTxHash` | Incoming transaction | Failed to associate incoming TX hash | Incoming tx association failed FailedToAssociateIncomingTxHash | | `EventId.InternalTransaction_Completed` | Internal transaction | Completed | Internal Transaction Completed | | `EventId.InternalTransaction_Submitted` | Internal transaction | Submitted | Internal Transaction Submitted | | `EventId.OutgoingTransaction_Completed` | Outgoing transaction | Completed | Outgoing Transaction Completed | | `EventId.OutgoingTransaction_Submitted` | Outgoing transaction | Submitted | Outgoing Transaction Submitted | | `EventId.Transaction_AlertedByAml` | Transaction | Flagged by AML | Transaction Alerted by AML | | `EventId.Transaction_AmlFailed` | Transaction | AML failed | Transaction AML Failed | | `EventId.Transaction_AmlScreeningBlockingPeriodTimedOut` | Transaction | AML screening blocking period timed out | Transaction AML screening blocking period timed out | | `EventId.Transaction_AmlResultRescreened` | Transaction | AML result rescreened | Transaction AML Result Rescreened | | `EventId.Transaction_ApprovedBy2ndTier` | Transaction | Approved by 2nd tier | Transaction Approved by 2nd Tier | | `EventId.Transaction_AuthorizationRequestInitiated` | Transaction | Authorization request initiated | Transaction Authorization Request Initiated | | `EventId.Transaction_BlockedByPolicy` | Transaction | Blocked by TAP | Transaction Blocked by Policy | | `EventId.Transaction_BypassedAml` | Transaction | Bypassed AML | Transaction Bypassed AML | | `EventId.Transaction_Cancelled` | Transaction | Canceled | Transaction Cancelled | | `EventId.Transaction_Completed` | Transaction | Completed | Transaction Completed | | `EventId.Transaction_ConfirmationThresholdOverridden` | Transaction | Confirmation threshold overridden | Transaction Confirmation Threshold Overridden | | `EventId.Transaction_DeclinedBy2ndTier` | Transaction | Declined by 2nd tier | Transaction Declined by 2nd Tier | | `EventId.Transaction_FundsUnfrozen` | Transaction | Funds unfrozen | Transaction Funds Unfrozen | | `EventId.Transaction_NoteChanged` | Transaction | Note changed | Transaction Note Changed | | `EventId.Transaction_Rejected` | Transaction | Rejected | Transaction Rejected | | `EventId.Transaction_RejectedByAml` | Transaction | Rejected by AML | Transaction Rejected by AML | | `EventId.Transaction_Signed` | Transaction | Signed | Transaction Signed | | `EventId.Transaction_Submitted` | Transaction | Submitted | Transaction Submitted | | `EventId.Transaction_TransactionRejected` | Transaction | Rejected | Transaction Transaction Rejected | | `EventId.ExternalTransaction_Completed` | Transaction | Completed | External Transaction Completed | | `EventId.ExternalTransaction_Submitted` | Transaction | Submitted | External Transaction Submitted | | `EventId.Transaction_ScreeningIncomingStarted` | Transaction | Screening incoming started | Transaction Screening incoming started | | `EventId.Transaction_AmlIncomingStarted` | Transaction | AML Incoming Started | Transaction AML Incoming Started | | `EventId.Transaction_AmlIncomingCompleted` | Transaction | AML Incoming Completed | Transaction AML Incoming Completed | | `EventId.Transaction_AmlIncomingFailed` | Transaction | AML Incoming Failed | Transaction AML Incoming Failed | | `EventId.Transaction_AmlIncomingInBackground` | Transaction | AML Incoming in background | Transaction AML Incoming in background | | `EventId.Transaction_TrIncomingStarted` | Transaction | TR Incoming started | Transaction TR Incoming started | | `EventId.Transaction_TrIncomingCompleted` | Transaction | TR Incoming completed | Transaction TR Incoming completed | | `EventId.Transaction_TrIncomingFailed` | Transaction | TR Incoming failed | Transaction TR Incoming failed | | `EventId.Transaction_ScreeningIncomingCompleted` | Transaction | Screening incoming completed | Transaction Screening incoming completed | # Wallets | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.ColdWallet_DeviceHasLessThan10_RemainingSignatures` | Cold wallet | Requires signatures | Cold Wallet Device has less than 10% remaining signatures | | `EventId.VaultAccount_Added` | Vault | Created | Vault Account Added | | `EventId.VaultAccount_AddedInBulk` | Vault | Bulk created | Vault Account Added in Bulk | | `EventId.VaultAccount_Archived` | Vault | Archived | Vault Account Archived | | `EventId.VaultAccount_DisabledAutoFueling` | Vault | Turned off auto-fueling | Vault Account Disabled Auto Fueling | | `EventId.VaultAccount_EnabledAutoFueling` | Vault | Turned on auto-fueling | Vault Account Enabled Auto Fueling | | `EventId.VaultAccount_Renamed` | Vault | Renamed | Vault Account Renamed | | `EventId.VaultAccount_SetHiddenOnUi` | Vault | Hidden | Vault Account Set Hidden On UI | | `EventId.VaultAccount_SetVisibleOnUi` | Vault | Made visible | Vault Account Set Visible On UI | | `EventId.VaultAccount_Unarchived` | Vault | Unarchived | Vault Account Unarchived | | `EventId.VaultDepositAddress_ChangedDescription` | Wallet | Changed deposit address description | Vault deposit address Changed description | | `EventId.VaultWallet_Added` | Wallet | Added | Vault wallet Added | | `EventId.VaultWallet_AddedInBulk` | Wallet | Bulk added | Vault wallet Added in Bulk | | `EventId.VaultWallet_Archived` | Wallet | Archived | Vault wallet Archived | | `EventId.VaultWallet_Unarchived` | Wallet | Unarchived | Vault wallet Unarchived | | `EventId.BackupAndRecovery_SentVerifyPassphraseAlert` | Mobile passphrase | Sent verification alert | Backup And Recovery Sent Verify Passphrase Alert | | `EventId.BackupAndRecovery_VerifiedPassphrase` | Mobile passphrase | Verified passphrase | Backup And Recovery Verified Passphrase | | `EventId.HardKeyRecoveryProcess_RequestSubmitted` | Key backup | Requested | Hard Key Recovery Process Request Submitted | | `EventId.MobileKey_BackedUp` | Mobile passphrase | Backed up | Mobile Key Backed Up | | `EventId.WorkspaceKeyBackup_ApprovedByAdmin` | Key backup | Approved | Workspace key backup Approved by admin | | `EventId.WorkspaceKeyBackup_ApprovedByAdminQuorum` | Key backup | Approved by quorum | Workspace key backup Approved by admin quorum | | `EventId.WorkspaceKeyBackup_Cancelled` | Key backup | Canceled | Workspace key backup Cancelled | | `EventId.WorkspaceKeyBackup_InternalErrorWhenGeneratingTheBackup` | Key backup | Error generating key backup | Workspace key backup Internal error when generating the backup | | `EventId.WorkspaceKeyBackup_RequestSubmitted` | Key backup | Requested | Workspace key backup Request Submitted | | `EventId.WorkspaceKeyBackup_TheProcessHasBeenRejectedByOnOfTheAdmins` | Key backup | Rejected | Workspace key backup The process has been rejected by on of the admins | | `EventId.WorkspaceKeyBackup_TheProcessHasBeenRejectedByOneOfTheAdmins` | Key backup | Rejected | Workspace key backup The process has been rejected by one of the admins | | `EventId.WorkspaceKeyBackup_TheWorkspaceOwnerHasInitiatedTheProcess` | Key backup | Initiated | Workspace key backup The workspace owner has initiated the process | | `EventId.WorkspaceKeyBackup_MarkedAsIncomplete` | Key backup | Marked as incomplete | Workspace key backup Marked as incomplete | | `EventId.WorkspaceKeyBackup_MarkedAsCompleted` | Key backup | Marked as completed | Workspace key backup Marked as completed | | `EventId.WorkspaceKeyBackup_PendingApproval` | Key backup | Pending approval | Workspace key backup Pending approval | | `EventId.WorkspaceKeyBackup_BackupSent` | Key backup | Backup sent | Workspace key backup Backup sent | | `EventId.GasStation_LowFunds` | Gas station | Low funds | Gas Station Low funds | | `EventId.GasStation_VaultChanged` | Gas station | Changed vault | Gas Station Vault changed | | `EventId.GasStation_SweepTransactionInitiated` | Gas station | Initiated sweep | Gas Station Sweep transaction initiated | | `EventId.GasStation_FundsDeposited` | Gas station | Deposited funds | Gas Station Funds deposited | | `EventId.ExternalWallet_Added` | Wallet | Added | External wallet Added | | `EventId.ExternalWallet_Removed` | Wallet | Removed | External wallet Removed | | `EventId.MyWallet_Added` | My wallet | Added | My wallet Added | | `EventId.MyWallet_Removed` | My wallet | Removed | My wallet Removed | # Web3 | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.Allowance_AmountModified` | Allowance | Changed | Allowance Amount Modified | | `EventId.Contract_Added` | Contract | Added | Contract Added | | `EventId.Contract_Removed` | Contract | Removed | Contract Removed | | `EventId.FireblocksExtensionConnected_ExtensionConnected` | Fireblocks browser extension | Connected | Fireblocks extension connected Extension Connected | | `EventId.FireblocksExtensionUpdated_ExtensionUpdated` | Fireblocks browser extension | Updated | Fireblocks extension updated Extension Updated | | `EventId.FireblocksExtensionDisconnected_ExtensionDisconnected` | Fireblocks browser extension | Disconnected | Fireblocks extension disconnected Extension Disconnected | | `EventId.Nft_MarkedAsSpam` | NFT | Marked as spam | NFT Marked as spam | | `EventId.Nft_MarkedAsNotSpam` | NFT | Marked as not spam | NFT Marked as not spam | # Whitelist | Event ID | Subject | Event | Notification Subject | | - | - | - | - | | `EventId.Address_Removed` | Whitelisted address | Removed | Address Removed | | `EventId.Address_Whitelisted` | Whitelisted address | Added | Address Whitelisted | | `EventId.AddressWhitelisting_ApprovedByAdmin` | Whitelisted address | Approved | Address Whitelisting Approved by admin | | `EventId.AddressWhitelisting_Cancelled` | Whitelisted address | Canceled | Address Whitelisting Cancelled | | `EventId.AddressWhitelisting_Rejected` | Whitelisted address | Rejected | Address Whitelisting Rejected | | `EventId.AddressWhitelisting_RequestSubmitted` | Whitelisted address | Requested | Address Whitelisting Request Submitted | # Introduction Source: https://developers.fireblocks.com/reference/background-retail-demo-application The Fireblocks Retail Demo application is designed to support Fireblocks clients by serving as a reference model for their integration processes. It embodies the best practices Fireblocks advocates for building secure, retail-facing solutions. This demo aims to accelerate and simplify the integration of Fireblocks into your projects, ensuring that you adhere to the recommended best practices when building on the Fireblocks platform. This article provides the necessary background on the considerations we had in mind when planning the demo application and developing its business logic. **Vault Structure** First, it's essential to understand how to best structure your Fireblocks workspace, vault accounts, and asset wallets to serve the requirements of a retail crypto application. There are generally [two main approaches for vault structuring](https://support.fireblocks.io/hc/en-us/articles/5253421857564-Fireblocks-Vault-structure-best-practices): Omnibus and Segregated. The [Omnibus](https://support.fireblocks.io/hc/en-us/articles/6986895761948-Account-and-wallet-structure) vault structure is usually the more fitting selection when planning a retail-facing product, as it offers a more efficient way to handle end-user deposits, balance management, and withdrawals in fewer transactions with better fee management than using a segregated structure. In our implementation, we have used a central Omnibus vault account to hold end-user funds. UTXO-based assets (e.g., BTC) are deposited by the end-users directly into the Omnibus vault using a dedicated deposit address for each user deposit, while account-based assets (e.g., ETH) are deposited to intermediate deposit vault accounts and later swept to the Omnibus vault. Additionally, we are using three designated withdrawal vaults to allow high withdrawal throughput. Using multiple withdrawal vaults allows for more robust withdrawal handling, as withdrawal requests might queue up into a long backlog due to an unconfirmed transaction of a specific asset from a withdrawal wallet. Such backlog scenarios should be handled with dedicated business logic, but generally speaking, having several withdrawal vaults ensures your application can maintain a steady and robust withdrawal throughput. The configuration of the Fireblocks workspace vaults and wallets is done by the application's [setup script](/reference/setup#setup-script) upon running the app for the first time. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/d0f9d4b234cf7084141dfad7ed2132f8e268290dc415af52d3948c8692f03d0b-image.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=947a9958290c3d8bfc8de292bacbb700) **End-user wallets** A retail application, potentially serving millions of end-users, must be able to create and operate millions of crypto deposit addresses, assign them to the correct end-user accounts in our internal ledger, and manage their balances correctly. When planning the application, we must consider which types of assets we will support (UTXO-based / Account-based / Tag or Memo-based), as different types require different wallet creation and management approaches. When an end-user logs in for the first time, we need to generate an account or wallet entity for that user that will hold all of their deposit addresses for the assets we support. Depending on the asset type, different business logic will occur in the backend when the user asks for a new deposit address. For UTXO or Tag/Memo-based assets (such as BTC, ADA, etc.), we will create a new dedicated **deposit address** inside the Omnibus vault account and assign it to the user account in our database (our internal ledger). For Account-based assets (such as ETH, SOL, etc.), we will create a new intermediate **vault account** and then create a deposit address inside this dedicated intermediate vault account whenever a user requests a new deposit address. One can improve this process by first checking if the user already has an unused vault account that doesn't yet have the requested asset's wallet. If such an account exists, we can use it to create the new deposit address instead of creating a new vault account each time the user requests a new address. Additionally, as the process of generating a deposit address for account-based assets will usually require two API calls to Fireblocks (one for creating the new vault account and one for generating the deposit address), it will be beneficial to add a job-like service that will pre-create vault accounts in batches. Once an end-user requests a new deposit address, the backend process will use a ready vault account when creating the deposit address and assign it to the relevant end-user (rename the vault account with a user reference ID and update the internal ledger accordingly). In our implementation, this business logic is mainly handled by the [VaultAccount service](https://github.com/fireblocks/retail-demo/blob/main/backend/src/service/fireblocks/vaultAccount.service.ts). **Deposit Management - Sweeping and UTXO Consolidation** Another aspect we must consider when planning a retail crypto application is the [sweeping](/reference/sweep-to-omnibus-1#sweeping) of account-based assets to the Omnibus vault account. For UTXO asset deposits, we must have a mechanism that will [consolidate](/reference/consolidate-utxos) our Omnibus wallet inputs (coming from end-user deposits) into larger UTXOs that allow more efficient processes down the line, such as internal rebalancing transactions and end-user withdrawals. **Sweeping** When designing the sweeping mechanism, we should take into account several considerations, such as: * Triggering (when should we sweep the intermediate vaults?) * Sorting (which vaults should be swept now?) * Gas balances (gas top-ups for token sweeping) * Asset sweeping order (sweep tokens before base assets) In our implementation, we trigger the sweeping mechanism based on a simple [time interval](https://github.com/fireblocks/retail-demo/blob/6f3dabed7cf7cab0435462fb17d409d62839cfad/backend/src/service/sweeping.service.ts#L13), but other possible and more complex approaches might be based on a set threshold of incoming deposits of account-based assets or based on monitoring gas rates in a given network to trigger the sweeping mechanism only when gas rates are relatively low, as a more cost-effective approach. As for sorting, we are querying the database for all user wallets that have a balance higher than a set threshold of a "sweepable" asset (account-based) and mapping the balances to the relevant Fireblocks vault account IDs so we can iterate through them and trigger the sweeping transactions accordingly. Based on your logic for triggering the sweeping process, you can also change the flow for sorting and selecting which intermediate vaults to sweep. The business logic of account-based asset sweeping is handled by the Sweeping service. **UTXO Consolidation** In Fireblocks, a UTXO-based asset transaction can consist of up to 250 inputs, and the default input selection mechanism selects inputs from the lowest to the highest value. These presets can lead to a situation where, in a high-volume crypto retail application that may receive numerous deposits of small amounts of tokens from end-users, the ever-growing list of small UTXOs will have the sufficient balance to support a transaction request from the Omnibus wallet with a large amount. Having a UTXO consolidation solution implemented correctly will help prevent such a situation and ensure the application's high throughput operation at all times. In our implementation, we've created two UTXO consolidation jobs, one of which is a backup process for the first routine job. We maintain a deposit counter for every UTXO asset the application supports in our database. Once the counter reaches 249 deposits, the routine consolidation job is triggered and will perform a consolidation transaction and reset the counter. The backup process runs every day and checks if any of the UTXO assets in the Omnibus wallet have 250 unspent inputs or more. This check is done with the [Fireblocks API](/reference/getunspentinputs) directly on the Omnibus vault account and adds another safeguard to the consolidation process. If 250 inputs or more are found, consolidation transactions will be triggered until that condition is no longer met. A consolidation transaction will have the Omnibus vault account as both the source and the destination. This will ensure that the Omnibus wallet will have inputs with high value available for future transactions. The business logic of UTXO consolidation is handled by the [consolidation service](https://github.com/fireblocks/retail-demo/blob/main/backend/src/service/consolidation.service.ts). **Internal Rebalancing** As mentioned above, a scalable retail crypto application must have several dedicated withdrawal vault accounts to support a high throughput of withdrawals. To function effectively, these vaults must maintain a sufficient balance of all supported assets at any given time. To achieve this, we need an internal rebalancing mechanism that ensures all withdrawal vaults are always funded. This mechanism can be developed programmatically using Fireblocks APIs, but in our implementation, we chose to leverage the new [automation](https://support.fireblocks.io/hc/en-us/articles/14873112741660-Automation) feature. This feature allows us to set up automatic triggers within Fireblocks if the asset's balance is lower than a set threshold after a withdrawal transaction is processed and completed. Below is an example of such an automation we've configured in our testnet workspace. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/45aedf6627765cac64468cd396c526cbb81854ea6699d69e4ad7cc941ea379e4-rebalance-automation.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=b21583e6b3c94fd305bf8b9df9d7c927) This rebalancing approach will require you to set a minimum and a maximum threshold per asset you support. The minimum threshold will be the triggering amount of a rebalancing transaction, and the maximum threshold will be the amount the withdrawal vault will be topped up to after the rebalancing transaction is completed. **End User Withdrawals** End-user withdrawals are a crucial phase in the lifecycle of a retail crypto application, deeply affecting the platform's reputation. To handle a high volume of withdrawal requests efficiently, utilizing multiple dedicated withdrawal vault accounts is recommended. Besides this strategy, there are key considerations for robust withdrawal logic. A solid withdrawal system should: * Validate User Balances: Check that the user's available balance is sufficient to cover the withdrawal amount and any associated fees. * Rotate Withdrawal Vaults: Incorporate a method, such as a random selection process, to alternate between different withdrawal vault accounts as transaction sources. In our approach, we've implemented a straightforward randomization logic. The application randomly picks a number corresponding to one of the available withdrawal vault accounts (e.g., selecting between 1 and 3). This determines the vault account ID used for the transaction source. [See our code example here](https://github.com/fireblocks/retail-demo/blob/6f3dabed7cf7cab0435462fb17d409d62839cfad/backend/src/utils/vaultConfig.ts#L37). The withdrawal flow is handled by the [transaction controller](https://github.com/fireblocks/retail-demo/blob/6f3dabed7cf7cab0435462fb17d409d62839cfad/backend/src/controller/transaction.controller.ts#L55) and the [transaction service](https://github.com/fireblocks/retail-demo/blob/6f3dabed7cf7cab0435462fb17d409d62839cfad/backend/src/service/fireblocks/transaction.service.ts). # About Bring Your Own Screening Source: https://developers.fireblocks.com/reference/bring-your-own-screening-check-developer-guide Set timeouts, activate Bring Your Own Screening, and submit ACCEPT or REJECT verdicts through the Fireblocks API. Bring Your Own Screening holds matching incoming and outgoing transactions until you submit a verdict, `ACCEPT` or `REJECT`, based on whatever you choose: a manual review, your own risk logic, or a provider Fireblocks doesn't integrate with natively. If no verdict arrives before your timeout, Fireblocks rejects the transaction, and that rejection can't be reversed. It is in Early Access as a premium add-on, available on Developer Sandbox, Testnet, and Mainnet workspaces; contact your Customer Success Manager to join. In the API it is called BYORK Light. ## What you can do through the API | Task | Console | API | | - | - | - | | Read the configuration | No | [`GET /screening/byork/config`](/api-reference/compliance/get-byork-light-configuration) | | Set timeouts | No | [`PUT /screening/byork/config/timeouts`](/api-reference/compliance/set-byork-light-timeouts) | | Activate | No | [`POST /screening/byork/config/activate`](/api-reference/compliance/activate-byork-light) | | Deactivate | No | [`POST /screening/byork/config/deactivate`](/api-reference/compliance/deactivate-byork-light) | | Submit a verdict | No | [`POST /screening/byork/verdict`](/api-reference/compliance/set-byork-light-verdict) | | Check a verdict | No | [`GET /screening/byork/verdict?txId={txId}`](/api-reference/compliance/get-byork-light-verdict) | | Configure the Trigger (pre-screening rules) | No | No: open a support ticket | ## How it works 1. Your Trigger (pre-screening rules) decides whether a transaction is held for your verdict (`SCREEN`) or skips this step (`PASS`). 2. A held transaction waits in `PENDING_AML_SCREENING`. There is no dedicated notification for this step: poll the transaction or watch for its next `transaction.status.updated` event. 3. You submit `ACCEPT` or `REJECT`. `ACCEPT` continues the transaction; `REJECT` or a timeout rejects it with `subStatus` `REJECTED_AML_SCREENING`, or `AUTO_FREEZE` if your policy freezes instead. ## Setup 1. **Get the feature enabled** by your Customer Success Manager. 2. **Set your timeouts.** Leave enough margin for your team or system to respond reliably. | Field | Default | Minimum | Maximum | | - | - | - | - | | `incomingTimeoutSeconds` | 3600 | 10 | 604800 (7 days) | | `outgoingTimeoutSeconds` | 3600 | 10 | Your access token's remaining lifetime minus 1800 seconds | 3. Include at least one field. The read, activate, and deactivate responses always show `timeoutRangeOutgoing.maxSeconds` as 604800, a static upper bound; the response to setting timeouts shows the enforced maximum. 4. **Configure your Trigger (pre-screening rules)** by opening a support ticket; see [Trigger fields](#trigger-fields). 5. **Track held transactions** by polling or listening for `transaction.status.updated`. 6. **Test in a sandbox workspace**, including how your integration handles a timeout. 7. **Activate.** Matching transactions are held from then on. Deactivating pauses the step without removing your configuration; while inactive, every transaction skips it. After going live, monitor how long your verdicts take against your timeouts. The configuration returns: ```json theme={"system"} { "active": false, "incomingTimeoutSeconds": 3600, "outgoingTimeoutSeconds": 3600, "provider": "BYORK_LITE", "lastUpdate": "2026-03-01T00:00:00.000Z", "timeoutRangeIncoming": { "minSeconds": 10, "maxSeconds": 604800 }, "timeoutRangeOutgoing": { "minSeconds": 10, "maxSeconds": 604800 } } ``` ## Submit verdicts Use an `Idempotency-Key` header to avoid duplicate submissions. ```ts theme={"system"} const { data } = await fireblocks.compliance.setByorkVerdict({ byorkVerdictRequest: { txId, verdict: "ACCEPT" }, idempotencyKey: `verdict-${txId}`, }); console.log(data.status, data.message); // "COMPLETED", "Verdict ACCEPT applied" ``` You can submit a verdict before the transaction reaches the waiting step, for example while AML/KYT is still running. Fireblocks stores it as `PRE_ACCEPTED` and applies it when the transaction arrives. | Verdict status | Meaning | | - | - | | `PRE_ACCEPTED` | Submitted before the transaction reached the waiting step; applied when it does. | | `PENDING` | The transaction is waiting for your verdict. | | `RECEIVED` | Your verdict is final and is being applied. | | `COMPLETED` | Your verdict is applied, and the transaction continues. | ## Trigger fields Rules are evaluated in order; the first match decides. Omitted fields match any value. Fireblocks Support configures them. | Field | Description | | - | - | | `action` | Required. `SCREEN` holds the transaction; `PASS` skips this step. | | `direction` | `INBOUND` or `OUTBOUND`. | | `sourceType`, `sourceSubType`, `sourceId`, `sourceAddress` | Match on the source, for example `sourceType` `VAULT_ACCOUNT` or `EXCHANGE`. | | `destType`, `destSubType`, `destId`, `destAddress` | Match on the destination. | | `sourceWorkspaceName` | Source workspace name, for incoming transactions. | | `destWorkspaceName` | Destination workspace name, for outgoing transactions. | | `asset` | Asset ID, for example `BTC`. | | `amount` | `{ "range": { "min": "1000", "max": "50000" }, "currency": "USD" }`. `currency` is `USD` or `NATIVE`; `min` and `max` are optional. | | `operation` | Transaction operation type. | | `isDefault` | `true` for the rule that applies when no other rule matches. | ## Errors | Status | Meaning | What to do | | - | - | - | | `400` | The feature isn't enabled, or the request is invalid. | Check enablement and the request body. | | `404` | No verdict exists for this transaction (check a verdict only). | Check the `txId`, and that the transaction has reached this step. | | `409` | A verdict was already submitted, screening is complete, or the screening state is temporarily inconsistent. | If a verdict exists or screening is complete, don't retry: it's final. Otherwise, retry after a short delay. | | `425` | The transaction isn't found yet; screening hasn't started. | Retry after a short delay. | ## Limitations * **A verdict is final**, and a timeout rejection can't be reversed. * **No Console flow;** the Trigger (pre-screening rules) is set by Fireblocks Support. * **Earlier integrations** on BYORK Super Light use [`POST /screening/aml/verdict/manual`](/api-reference/compliance/set-aml-verdict-byork-super-light), which covers incoming transactions only and will be deprecated when Super Light is retired. Use BYORK Light for new integrations. * **A REJECT verdict** on an incoming transfer freezes the funds; on an outgoing transfer it rejects the transaction. * **With AML/KYT configured**, only transactions that AML/KYT bypassed, accepted, or alerted reach this check. The Trigger cannot depend on the AML/KYT result. One decision per transaction, also with several destinations. A timed-out hold is rejected and cannot be reversed. ## On the Help Center For a conceptual overview, see [About Bring Your Own Screening](https://support.fireblocks.io/hc/en-us/articles/30614347120156-About-Bring-Your-Own-Screening). # Caching Signatures Source: https://developers.fireblocks.com/reference/caching-signatures Some DeFi apps expect to receive the same signature for separate consecutive messages. Signature caching allows the most recent signature to be returned based on the content and the source, including the workspace ID, Vault account, and asset, or derivation path. This signature may be returned repeatedly, skipping additional approval and signing processes. Signature caching is enabled for all users by default using [WalletConnect](https://support.fireblocks.io/hc/en-us/articles/5403817784732-Connecting-to-Web3-using-WalletConnect) or the [Fireblocks Chrome Extension](https://fireblocks.zendesk.com/hc/en-us/articles/5403962226332). However, you can opt out by [contacting Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new). Signature caching also works with [Typed Messages](/docs/typed-message-signing-1) and [Raw Signing](/docs/raw-signing) via the Fireblocks API. # Code Examples Source: https://developers.fireblocks.com/reference/code-examples-2 # Bitcoin Arbitrary Message > [!TIP] > > BTC arbitrary message signing can also be performed by Typed Message signing, which is considered more secure. The example below illustrates the use of the Raw Signing feature for Bitcoin arbitrary message signing: ```javascript theme={"system"} import { readFileSync } from 'fs'; import { Fireblocks, BasePath, TransactionOperation, TransferPeerPathType, TransactionRequest, TransactionResponse, FireblocksResponse, TransactionStateEnum, CreateTransactionResponse } from "@fireblocks/ts-sdk"; import { createHash } from "crypto"; const FIREBLOCKS_API_SECRET_PATH = ""; const API_KEY = "" // Initialize a Fireblocks API instance with local variables const fireblocks = new Fireblocks({ apiKey: API_KEY, basePath: BasePath.US, // Basepath.Sandbox for the sandbox env secretKey: readFileSync(FIREBLOCKS_API_SECRET_PATH, "utf8"), }); const transactionPayload: TransactionRequest = { assetId: "BTC", operation: TransactionOperation.Raw, source: { type: TransferPeerPathType.VaultAccount, id: "0", }, note: ``, extraParameters: { rawMessageData: {}, }, }; let txInfo: any; const getTxStatus = async (txId: string): Promise => { try { let response: FireblocksResponse = await fireblocks.transactions.getTransaction({ txId }); let tx: TransactionResponse = response.data; let messageToConsole: string = `Transaction ${tx.id} is currently at status - ${tx.status}`; console.log(messageToConsole); while (tx.status !== TransactionStateEnum.Completed) { await new Promise((resolve) => setTimeout(resolve, 3000)); response = await fireblocks.transactions.getTransaction({ txId }); tx = response.data; switch (tx.status) { case TransactionStateEnum.Blocked: case TransactionStateEnum.Cancelled: case TransactionStateEnum.Failed: case TransactionStateEnum.Rejected: throw new Error( `Signing request failed/blocked/cancelled: Transaction: ${tx.id} status is ${tx.status}`, ); default: console.log(messageToConsole); break; } } while (tx.status !== TransactionStateEnum.Completed); return tx; } catch (error) { throw error; } }; const rawSign = async ( message: string, ): Promise => { const wrappedMessage = "\x18Bitcoin Signed Message:\n" + String.fromCharCode(message.length) + message; const hash = createHash("sha256").update(wrappedMessage, "utf8").digest(); const content = createHash("sha256").update(hash).digest("hex"); //@ts-ignore transactionPayload.extraParameters.rawMessageData = { messages: [ { content, bip44addressIndex: 0, }, ], }; transactionPayload.note = `BTC Message: ${message}`; try { const transactionResponse = await fireblocks.transactions.createTransaction( { transactionRequest: transactionPayload, }, ); //@ts-ignore console.log(transactionPayload.extraParameters.rawMessageData); const txId = transactionResponse.data.id; if (!txId) { throw new Error("Transaction ID is undefined."); } txInfo = await getTxStatus(txId); console.log(JSON.stringify(txInfo, null, 2)); const signature = txInfo.signedMessages[0].signature; console.log(JSON.stringify(signature)); const encodedSig = Buffer.from([Number.parseInt(signature.v, 16) + 31]).toString("hex") + signature.fullSig; console.log( "Encoded Signature:", Buffer.from(encodedSig, "hex").toString("base64"),); return } catch (error) { console.error(error); } }; rawSign("My message23"); ``` ```javascript theme={"system"} import { createHash } from "crypto"; import * as fs from "fs" import * as path from "path" import { FireblocksSDK, PeerType, TransactionOperation, TransactionStatus } from "fireblocks-sdk"; const apiKey = "" const apiSecretPath = "" const apiSecret = fs.readFileSync(path.resolve(__dirname, apiSecretPath), "utf8"); const fireblocks = new FireblocksSDK(apiSecret, apiKey); async function signArbitraryMessage(fireblocks: FireblocksSDK, vaultAccountId: string, message: string, bip44addressIndex = 0) { const wrappedMessage = "\x18Bitcoin Signed Message:\n" + String.fromCharCode(message.length) + message; const hash = createHash('sha256').update(wrappedMessage, 'utf8').digest(); const content = createHash('sha256').update(hash).digest("hex"); const { status, id } = await fireblocks.createTransaction({ operation: TransactionOperation.RAW, assetId: "BTC", source: { type: PeerType.VAULT_ACCOUNT, id: vaultAccountId }, note: `BTC Message: ${message}`, extraParameters: { rawMessageData: { messages: [{ content, bip44addressIndex }] } } }); let txInfo; let currentStatus = status; while(currentStatus != TransactionStatus.COMPLETED && currentStatus != TransactionStatus.FAILED) { try { console.log("keep polling for tx " + id + "; status: " + currentStatus); txInfo = await fireblocks.getTransactionById(id); currentStatus = txInfo.status; } catch (err) { console.log("err", err); } await new Promise(r => setTimeout(r, 1000)); }; const signature = txInfo.signedMessages[0].signature; console.log(JSON.stringify(signature)); const encodedSig = Buffer.from([signature.v + 31]).toString("hex") + signature.fullSig; console.log("Encoded Signature:", Buffer.from(encodedSig,"hex").toString("base64")); } signArbitraryMessage(fireblocks, "0", "INSERT TEXT HERE"); ``` ```python theme={"system"} from fireblocks.client import Fireblocks from fireblocks.client_configuration import ClientConfiguration from fireblocks.base_path import BasePath from fireblocks.models.transaction_request import TransactionRequest from fireblocks.models.destination_transfer_peer_path import DestinationTransferPeerPath from fireblocks.models.source_transfer_peer_path import SourceTransferPeerPath from fireblocks.models.transfer_peer_path_type import TransferPeerPathType from fireblocks.models.transaction_request_amount import TransactionRequestAmount from pprint import pprint # load the secret key content from a file with open('your_secret_key_file_path', 'r') as file: secret_key_value = file.read() # build the configuration configuration = ClientConfiguration( api_key="your_api_key", secret_key=secret_key_value, base_path=BasePath.Sandbox, # or set it directly to a string "https://sandbox-api.fireblocks.io/v1" ) # Enter a context with an instance of the API client with Fireblocks(configuration) as fireblocks: transaction_request: TransactionRequest = TransactionRequest( asset_id="BTC", amount=TransactionRequestAmount("0.1"), source=SourceTransferPeerPath( type=TransferPeerPathType.VAULT_ACCOUNT, id="0" ), destination=DestinationTransferPeerPath( type=TransferPeerPathType.VAULT_ACCOUNT, id="1" ), note="Your first transaction!" ) # or you can use JSON approach: # # transaction_request: TransactionRequest = TransactionRequest.from_json( # '{"note": "Your first transaction!", ' # '"assetId": "BTC", ' # '"source": {"type": "VAULT_ACCOUNT", "id": "0"}, ' # '"destination": {"type": "VAULT_ACCOUNT", "id": "1"}, ' # '"amount": "0.1"}' # ) try: # Create a new transaction future = fireblocks.transactions.create_transaction(transaction_request=transaction_request) api_response = future.result() # Wait for the response print("The response of TransactionsApi->create_transaction:\n") pprint(api_response) # to print just the data: pprint(api_response.data) # to print just the data in json format: pprint(api_response.data.to_json()) except Exception as e: print("Exception when calling TransactionsApi->create_transaction: %s\n" % e) ``` 3. Start by creating a transaction of type **RAW**, shown under `operation` (JS) or by using `create_raw_transaction()` (PY). 4. Now, specify the source who is signing the message, which is the default vault (`Id 0`) in our case. After sending the transaction for signing, wait for its completion so you can retrieve the signature. The loop in the example above waits for the status of the transaction to be `COMPLETED`, or until an issue arises. Once the transaction is complete, we print the full signature by accessing the transaction object `signedMessages`, then `signature`. # Common errors Source: https://developers.fireblocks.com/reference/common-errors # Unexpected physicalDeviceId When thrown by any SDK function, this error indicates that the `physicalDeviceId` associated with the `deviceId` in Fireblocks and the `physicalDeviceId` sent with the request are not identical. The `physicalDeviceId` is an ID the SDK provisions when the SDK is initialized on a new web browser or mobile device. After the `generateMPCKeys` function generates key shares or an end user completes the recovery procedure, Fireblocks assigns the `deviceId` to the `physicalDeviceId`. This means that the same `deviceId` cannot issue requests (e.g., sign transactions) from any other SDK initialization. > [!NOTE] > > **Reminder** > > The SDK initialization occurs with a specific `deviceId`. Below are some scenarios that may result in an **Unexpected physicalDeviceId** exception. ## Unexpected physicalDeviceId after recovery As described [here](https://ncw-developers.fireblocks.com/docs/backup-recovery-1#additional-recovery-info), encrypted key share backups are associated with a corresponding `deviceId`. When an end-user recovers a key share from a new device (i.e., a different `physicalDeviceId` than their original device), the `deviceId` of the original device is no longer associated with the key share. ### Example If Device A (`physicalDeviceId: pd1` and `deviceId: d1`) generates a key share, and Device B (`physicalDeviceId: pd2`) recovers Device A's key share, the NCW SDK will throw the **Unexpected physicalDeviceId** error message when Device A attempts to sign a transaction since `pd1` is no longer associated with `d1`. ## Unexpected physicalDeviceId after key generation abruptly stopped During the key generation process, Fireblocks associates the `physicalDeviceId` with the `deviceId`. If the process is interrupted and then restarted with the same `deviceId` but a different `physicalDeviceId`, the NCW SDK will throw the **Unexpected physicalDeviceId** error message. ### Examples * Reinstalling the app, clearing the storage, and then calling `generateMPCKeys` again * Calling `generateMPCKeys` from another device # Configure Policies Source: https://developers.fireblocks.com/reference/configure-transaction-authorization-policy # The Policy Rules structure The Policy Rule object represents a single unit of logic that Policies enforce. Understanding the structure and constraints of this object is crucial to working with Policy Editor APIs. The same object is used when working with Policy Drafts. To publish a single rule or a set of rules, pass an array inside the request body that holds one or more Policy Rule objects. Each Policy Rule object in the array should have the following mandatory fields: | Field | Type | Values | Description | | - | - | - | - | | Action | String | ALLOW, BLOCK, 2-TIER | Defines what occurs when a transaction meets the rule's criteria. | | Asset | String | | Defines the type of asset being transacted. Use “\*” for all assets, or specify a specific `assetID` string that can be obtained using the [List all asset types supported by Fireblocks endpoint](/api-reference/blockchains-&-assets/list-assets-legacy). | | amountCurrency | String | USD, EURO, NATIVE | Limits the amount of any asset users can transfer based on their NATIVE value or based on their USD or EURO equivalents. | | Src and dst | String | | Defines source and destination accounts the rule allows transfers to originate from and be sent to. | | amountScope | String | SINGLE\_TX, TIMEFRAME | This limit applies to a single transaction or to all transactions within the defined time period. | | Amount | String | | | | periodSec | Number | | Time period in seconds which applies to the `amountScope` field to accumulate transferred amounts in transactions that match the rule, until the total exceeds the value you specify under Minimum. When the specified amount is reached within that period, whether by one or many transactions, further transactions in that period either fail or require more approvals. - **Note:** If `amountScope` = “SINGLE\_TX”, meaning that the rule should not cover a Time period, set `periodSec` = “0” | | Type | String | TRANSFER (Default) | The default value of TRANSFER is fixed and the value cannot be changed. If you wish to change the rule to apply on any of the other possible transaction types, add the `transactionType` key. \*\*Note:\*\*Additional mandatory fields apply when changing the `transactionType`. | # Objects within the Policy Rule ## externalDescriptor Each Policy rule receives a unique string ID identifying it. A “rule” is an object managed separately in Fireblocks as part of the Policy. The `externalDescriptor` key is an object holding that string ID value. This value is used to reference the specific rule when you wish to overwrite an existing rule using the [Send publish request for a set of policy rules endpoint](/api-reference/policy-editor-beta/send-publish-request-for-a-set-of-policy-rules). It is also referenced after the Policy Validation process is executed, and its response is returned under the `checkResult` object. ## Operators Pass one or more `userID` strings inside an array of strings as the value for the “users” key in order to represent the users that are allowed to sign the transactions which are relevant under the scope of this rule. Use the [List users endpoint](/api-reference/users/list-users) to obtain the user IDs. ```json theme={"system"} "operators": { "usersGroups": ["userId1", "userId2", "userId3"] }, ``` Alternatively, to configure a rule where all users are allowed, replace the “users” key with the "wildcard" key and pass `*`. ```json theme={"system"} "operators": { "wildcard": "*" }, ``` Passing an Array of “userGroups” instead of “users” is optional. Use the [List user groups endpoint](/reference/getusergroups) to obtain the `userGroups` IDs. ```json theme={"system"} "operators": { "userGroups": ["userGroupID1", "userGroupID2"] }, ``` Check the example section below for payloads that have rules defined with either option. ## transactionType It is possible to pass the transactionType key with either of the below values to configure a rule for the following types: * TRANSFER - Default. Transfers funds from one account to another. * CONTRACT\_CALL - calls a smart contract. * APPROVE - enables the approve function to be used for a smart contract to withdraw from a designated wallet. [Learn more](https://support.fireblocks.io/hc/en-us/articles/4404616097426-Approve-Transaction-Amount-Cap). * MINT - performs a mint operation (increases supply) on a supported token. * BURN - performs a burn operation (reduces supply) on a supported token. * STAKE - allows you to allocate and lock assets supported for staking to accrue rewards. [Learn more](https://support.fireblocks.io/hc/en-us/articles/4417214701970-Staking-on-Fireblocks). * RAW - an off-chain message used to sign any message with your private key. * TYPED\_MESSAGE - an off-chain message type that follows a predefined format and used to sign specific ETH or BTC messages. Each of the Policy rule types may have different mandatory fields that are seen inside the endpoint’s [API reference documentation](/api-reference/policy-editor-beta/send-publish-request-for-a-set-of-policy-rules). For example, for the TRANSFER type, you can use any of the following sources or destinations: * `*` * VAULT * ONE\_TIME\_ADDRESS * EXCHANGE * UNMANAGED * NETWORK\_CONNECTION * FIAT\_ACCOUNT > [!NOTE] > > Using ONE\_TIME\_ADDRESS, UNMANAGED & NETWORK\_CONNECTION is only possible under DST. ## SRC & DST Standard Transfer rules as well as Contract Call rules limit the activity for specific sources or destinations. Exceptions to these rules, where the rule should not include a destination limitation, are Typed Message, RAW and Burn rule types. The SRC & DST object structures should be an array of arrays. Each array can hold an array of either: * A single key - “wildcard”, as the type of address should not be indicated * Two keys - ID and Type * Three keys - ID, Type, and Subtype of the address ```json theme={"system"} "dst": { "ids": [ [ "*", "UNMANAGED" ], [ "*", "EXCHANGE", "*" ], [ "*", "NETWORK_CONNECTION", "*" ] ] }, "src": { "ids": [ [ "*", "VAULT", "*" ], [ "*", "EXCHANGE", "KRAKEN" ] ] }, ``` Check the example section below for full payloads that have rules defined with either option. # Publishing, approving and validating Policy rules A Policy should normally have more than a single rule to govern the operations of the workspace. Therefore, multiple Policy Rule objects are passed inside the “rules” object. The [Send publish request for a set of policy rules endpoint](/api-reference/policy-editor-beta/send-publish-request-for-a-set-of-policy-rules) should receive a payload of your entire Policy. Check the example section below for payloads that have multiple rules defined. # Publish a Poli​​cy using an SDK To publish your Policy, use the `publishPolicyRules` SDK method and include the Policy Rule object. In the following example, you can see the passed object named “rule”. As mentioned, a typical Policy includes rules which are sent under the same array as a separate object. Here is a code sample for configuring a single rule allowing ETH transactions: ```javascript theme={"system"} async function publishPolicyRules(){[ rule = [ { "action": "ALLOW", "asset": "ETH", "amountCurrency": "USD", "src": { "ids": [ [ "*" ] ] }, "dst": { "ids": [ [ "*" ] ] }, "amountScope": "SINGLE_TX", "amount": "0", "periodSec": 0, "type": "TRANSFER", "operators": { "wildcard": "*" } } ] ] console.log(typeof(rule)); const publishPolicyRules = await fireblocks.publishPolicyRules(rule); console.log(JSON.stringify(publishPolicyRules, null, 2)); } ``` Check the example section below for payloads that have multiple rules defined. ## Edit an Existing Policy rule Using the [Send publish request for a set of policy rules endpoint](/api-reference/policy-editor-beta/send-publish-request-for-a-set-of-policy-rules), you can edit existing rules by passing the Policy Rule object details and adding the externalDescriptor ID of an existing rule that you wish to overwrite. Get the externalDescriptor from the [Get the active policy and its validation endpoint](/api-reference/policy-editor-beta/get-the-active-policy-and-its-validation). To edit multiple rules, pass the Policy Rule object and add each rule’s unique externalDescriptor in every rule object. # Approving the Policy change inside the Console Once you execute the code as the example above shows, your workspace Owner will receive a **Review Policy changes** notification in their Console. They will have to login and review the changes prior to getting the approval request on their mobile device. Below is the notification that the Owner should look for once logging into the Console and navigating to the **Settings** page: ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/8d77558-img2.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=07b53d8953fc786145a1b021bd0d9698) On the top-right side of the screen, the following will appear: ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/f5cbace-img3.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=152bf0bc4dd2decf57d50d06d98c40a9) # Customizing the Rules ## Time-Based Threshold Passing the `amountAggregation` object, together with the amountScope key (set to “TIMEFRAME”) and the `periodSec` (representing the time period in seconds), will configure the rule to match the aggregated value of transactions, the source and destination inclusion pattern, and the time period. ### Note: This is a mandatory ***number*** parameter that should be set to “0” if no Time Based Threshold is used in the rule. **Example**: Block transfers when a \$15,000 accumulation is reached over a 12-hour period (12hx60mx60s=43200 seconds)) ```json theme={"system"} "action": "BLOCK", "amount": "15000", "amountCurrency": "USD", "transactionType": "TRANSFER", "allowedAssetTypes": "FUNGIBLE", "amountAggregation": { "operators": "ACROSS_ALL_MATCHES", "dstTransferPeers": "ACROSS_ALL_MATCHES", "srcTransferPeers": "ACROSS_ALL_MATCHES" }, "amountScope": "TIMEFRAME", "periodSec": 43200, ``` ## applyForApprove Passing this key with a True value, will configure a Contract\_Call rule to match for initial “Approve” transaction used in Web3 smart contracts. ```json theme={"system"} "applyForApprove": true, ``` ## applyForTypedMessage Passing this key with a True value, when configuring a Contract\_Call rule, will match interactions with decentralized apps (dApps) that require signing EIP-712 or ETH personal messages. Alternatively, you can create a separate Typed\_Message type rule for that purpose only. Configuring this key is relevant for Contract\_Call rules when one-time address (OTA) and whitelisted destinations are used or for Off-chain messages that have no destination. ```json theme={"system"} "applyForTypedMessage": true, ``` ## rawMessageSigning Passing this object, will configure the rule to match specific Raw Signing messages by either their derivation paths(s) or their signing algorithm. ```json theme={"system"} "rawMessageSigning": { "algorithm": "MPC_ECDSA_SECP256K1", "derivationPath": { "path": [ 44,0,0,0,0 ] } }, ``` This is applicable when the rule is configured with the “All vaults” as the source. In the example below, the SRC object limits the source to the “Vault” type. Unlike the other types of sources allowed, vaults have a derivation path per each asset wallet. [See Policy Rules for RAW Signing](https://support.fireblocks.io/hc/en-us/articles/4413379762450-Raw-Signing). ```json theme={"system"} "src": { "ids": [ [ "*", "VAULT", "*" ] ] }, ``` Note that passing the “derivationPath” key is mandatory while passing “algorithm” is optional. # Policy Validation Fireblocks checks your rule submission for errors. Each rule receives a unique string ID identifying the rule. The rule ID is returned under the Policy response body inside the `externalDescriptor` parameter. Together with the details of the submitted rule(s), the Policy response object shows additional validation-related details under the status key and the `checkResult` object: * `status`: the status of the Policy operation * `checkResult`: an object showing the details of the check for each rule submitted * `errors`(1): a number indicating the total amount of errors found * `results`: an array holding the details of the validation per each rule submitted. Each validation result will have an index key that represents the position of the rule within the set of Policy rules. Each validation will contain an array of errors found within it. * `errors` (2): an array of objects, each reflecting the specific error details identified. Here is an example Response object showing the validation fields (the rule itself is redacted): ```json theme={"system"} { "status": "INVALID_CONFIGURATION", "checkResult": { "errors": 1, "results": [ { "index": 0, "externalDescriptor": "{\"id\":\"1e9cf057-028d-4657-8da1-b604800ed198\"}", "status": "failure", "errors": [ { "errorMessage": "INVALID_ASSET_TYPE", "errorCode": 2, "errorCodeName": "INVALID_PARAMETERS", "errorField": "allowedAssetTypes" } ] } ] } } ``` # Metadata response object Together with the details of the submitted rule(s), the status of the Policy submission, and the `checkResult` object, the Policy response object holds a few important metadata details: * `editedAt`: the timestamp of receiving the Policy edit request * `editedBy`: the user ID submitting the Policy * `publishedAt`: the timestamp of publishing the Policy * `publishedBy`: the user ID that published the Policy Example Metadata object details that are a part of the Policy Response object: ```yaml theme={"system"} "metadata": { "editedAt": 1708865838000, "editedBy": "a54eb4b7-6a90-2e26-50c1-94369aa00177", "publishedAt": 1708865838314, "publishedBy": "a54eb4b7-6a90-2e26-50c1-94369aa00177" } ``` # Policy examples * Transfer OTA * Transfer accumulated * Contract Call The following JSON sequence shows a set of transfer rules demonstrating the usage of `designatedSigners`, `authorizationGroups`, and operators (initiators) in an individual/group/combined configuration. Also, notice that `dstAddressType` matches the specific destination type. The flow in this example shows: * A separate handling of One-time Address destinations, requiring special approvals and signature. * Four consecutive rules that define permitted transfers to whitelisted destinations for: * Overall accumulation which blocks deviating transfers. * Requirement of special approvals for single transactions above a certain amount and between specific sets of venues. * Definition of the most permitted transfer rule for transactions that do not match preceding rules. ![](https://mintcdn.com/fireblocks-43c4b3ee/xYGyrtyC_XHEtVXh/images/docs/e13a587-img4.png?fit=max\&auto=format\&n=xYGyrtyC_XHEtVXh\&q=85\&s=1eee975b61d598b3e535ed6a29b7fc5f) ```json theme={"system"} { "draftResponse": { "draftId": "0f8f98d8-43d8-4b68-b167-9b1e35c99e2e", "status": "UNVALIDATED", "rules": [ { "dst": { "ids": [ [ "*" ] ] }, "src": { "ids": [ [ "*", "VAULT", "*" ] ] }, "type": "TRANSFER", "asset": "*", "action": "2-TIER", "amount": "0", "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 0, "amountScope": "SINGLE_TX", "amountCurrency": "USD", "dstAddressType": "ONE_TIME", "applyForApprove": false, "transactionType": "TRANSFER", "allowedAssetTypes": "FUNGIBLE", "designatedSigners": { "users": [ "316c2789-e8f0-45a3-9d2f-16cfec340a10" ], "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "externalDescriptor": "{\"id\":\"ca863088-718b-4516-820c-3d06e80c4aad\"}", "authorizationGroups": { "logic": "OR", "groups": [ { "th": 2, "users": [ "9e165261-cffc-4a7f-9f7e-3ed515cfbf16" ], "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] } ], "allowOperatorAsAuthorizer": false } }, { "dst": { "ids": [ [ "*" ] ] }, "src": { "ids": [ [ "*" ] ] }, "type": "TRANSFER", "asset": "*", "action": "BLOCK", "amount": "10000000", "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 86400, "amountScope": "TIMEFRAME", "amountCurrency": "USD", "dstAddressType": "WHITELISTED", "applyForApprove": false, "transactionType": "TRANSFER", "allowedAssetTypes": "FUNGIBLE", "amountAggregation": { "operators": "ACROSS_ALL_MATCHES", "dstTransferPeers": "ACROSS_ALL_MATCHES", "srcTransferPeers": "ACROSS_ALL_MATCHES" }, "externalDescriptor": "{\"id\":\"b4c22327-e0cb-4a8b-9d81-7e352ab4e213\"}" }, { "dst": { "ids": [ [ "*", "UNMANAGED" ], [ "*", "EXCHANGE", "*" ], [ "*", "NETWORK_CONNECTION", "*" ] ] }, "src": { "ids": [ [ "*", "VAULT", "*" ], [ "*", "EXCHANGE", "KRAKEN" ] ] }, "type": "TRANSFER", "asset": "*", "action": "2-TIER", "amount": "100000", "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 0, "amountScope": "SINGLE_TX", "amountCurrency": "USD", "dstAddressType": "WHITELISTED", "applyForApprove": false, "transactionType": "TRANSFER", "allowedAssetTypes": "FUNGIBLE", "externalDescriptor": "{\"id\":\"ea2a03cc-05da-4bdc-a119-4ba23798ed22\"}", "authorizationGroups": { "logic": "OR", "groups": [ { "th": 1, "users": [ "9e165261-cffc-4a7f-9f7e-3ed515cfbf16", "316c2789-e8f0-45a3-9d2f-16cfec340a10" ], "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] } ], "allowOperatorAsAuthorizer": false } }, { "dst": { "ids": [ [ "*" ] ] }, "src": { "ids": [ [ "*" ] ] }, "type": "TRANSFER", "asset": "*", "action": "ALLOW", "amount": "0", "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 0, "amountScope": "SINGLE_TX", "amountCurrency": "USD", "dstAddressType": "WHITELISTED", "applyForApprove": false, "transactionType": "TRANSFER", "allowedAssetTypes": "FUNGIBLE", "externalDescriptor": "{\"id\":\"3972a016-6903-4eb6-85f4-07192392f82f\"}" } ], "metadata": { "editedBy": "316c2789-e8f0-45a3-9d2f-16cfec340a10", "editedAt": "2024-02-18T16:19:51.000Z" } }, "validation": { "status": "SUCCESS", "checkResult": { "errors": 0, "results": [ { "index": 0, "externalDescriptor": "{\"id\":\"ca863088-718b-4516-820c-3d06e80c4aad\"}", "status": "ok", "errors": [] }, { "index": 1, "externalDescriptor": "{\"id\":\"b4c22327-e0cb-4a8b-9d81-7e352ab4e213\"}", "status": "ok", "errors": [] }, { "index": 2, "externalDescriptor": "{\"id\":\"ea2a03cc-05da-4bdc-a119-4ba23798ed22\"}", "status": "ok", "errors": [] }, { "index": 3, "externalDescriptor": "{\"id\":\"3972a016-6903-4eb6-85f4-07192392f82f\"}", "status": "ok", "errors": [] } ] } } } ``` The next JSON example demonstrates similar sets of rules which can be placed within your Policy, according to the first-match principle, to allow web3 interactions. The flow in this example shows: * A permissive rule for Contract Calls performed from a specific vault designated to whitelisted contracts * A Contract Call rule that allows other vaults to interact with whitelisted contracts upon approval * A rule that allows users to interact with non-whitelisted contracts with more restrictive approvals ![](https://mintcdn.com/fireblocks-43c4b3ee/Pe8CxJ47xNI_qfOs/images/docs/f5dcec8-img5.png?fit=max\&auto=format\&n=Pe8CxJ47xNI_qfOs\&q=85\&s=8f8e8eba692aad2c67a6416c1daca9d7) ```json theme={"system"} { "policy": { "rules": [ { "dst": { "ids": [ [ "*", "UNMANAGED", "CONTRACT" ] ] }, "src": { "ids": [ [ "0", "VAULT", "*" ] ] }, "type": "TRANSFER", "asset": "*", "action": "ALLOW", "amount": 0, "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 0, "amountScope": "SINGLE_TX", "amountCurrency": "USD", "dstAddressType": "WHITELISTED", "applyForApprove": true, "transactionType": "CONTRACT_CALL", "allowedAssetTypes": "FUNGIBLE", "applyForDeployment": false, "externalDescriptor": "{\"id\":\"3972a016-6903-4eb6-85f4-07192392f82f\"}", "applyForTypedMessage": true }, { "dst": { "ids": [ [ "*", "UNMANAGED", "CONTRACT" ] ] }, "src": { "ids": [ [ "*", "VAULT", "*" ] ] }, "type": "TRANSFER", "asset": "*", "action": "2-TIER", "amount": 0, "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 0, "amountScope": "SINGLE_TX", "amountCurrency": "USD", "dstAddressType": "WHITELISTED", "applyForApprove": true, "transactionType": "CONTRACT_CALL", "allowedAssetTypes": "FUNGIBLE", "applyForDeployment": false, "externalDescriptor": "{\"id\":\"059c0bb7-807a-4de2-a250-bc933e3c8969\"}", "authorizationGroups": { "logic": "OR", "groups": [ { "th": 1, "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] } ], "allowOperatorAsAuthorizer": false }, "applyForTypedMessage": true }, { "dst": { "ids": [ [ "*" ] ] }, "src": { "ids": [ [ "*", "VAULT", "*" ] ] }, "type": "TRANSFER", "asset": "*", "action": "2-TIER", "amount": 0, "operators": { "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] }, "periodSec": 0, "amountScope": "SINGLE_TX", "amountCurrency": "USD", "dstAddressType": "ONE_TIME", "applyForApprove": true, "transactionType": "CONTRACT_CALL", "allowedAssetTypes": "FUNGIBLE", "applyForDeployment": false, "externalDescriptor": "{\"id\":\"ee775834-e46c-4413-a713-de114c0193eb\"}", "authorizationGroups": { "logic": "OR", "groups": [ { "th": 2, "usersGroups": [ "5a325451-d970-4dd0-87e4-ae2c54936f4f" ] } ], "allowOperatorAsAuthorizer": false }, "applyForTypedMessage": true } ], "metadata": { "publishedBy": "316c2789-e8f0-45a3-9d2f-16cfec340a10", "publishedAt": 1710168706517 } }, "validation": { "status": "SUCCESS", "checkResult": { "errors": 0, "results": [ { "index": 0, "externalDescriptor": "{\"id\":\"3972a016-6903-4eb6-85f4-07192392f82f\"}", "status": "ok", "errors": [] }, { "index": 1, "externalDescriptor": "{\"id\":\"059c0bb7-807a-4de2-a250-bc933e3c8969\"}", "status": "ok", "errors": [] }, { "index": 2, "externalDescriptor": "{\"id\":\"ee775834-e46c-4413-a713-de114c0193eb\"}", "status": "ok", "errors": [] } ] } } } ``` # Consolidate UTXOs Source: https://developers.fireblocks.com/reference/consolidate-utxos If you regularly run operations on the Bitcoin blockchain, you will likely notice that the list of UTXOs in your wallets grows very quickly. This is especially common in situations where you have multiple addresses used to consolidate into an omnibus account or just as part of an ongoing operation. This can be a major problem for retail-facing operations. A process utilized by most companies is "consolidating UTXOs", or creating a transaction that will take many small unspent UTXOs and turn them into a single bigger UTXO. This is done by creating an "internal" transaction within your own vault account that takes the maximum amount of inputs (250) and turns them into a single output. Although Bitcoin is the most popular UTXO blockchain, this limitation applies to Bitcoin Cash, Bitcoin SV, Dash, Litecoin, ZCash, and a maximum of 110 inputs for Cardano. # Example The logic to decide which unspent UTXOs to use can be as simple or complex as you wish, but in this example, we will use any small unspent UTXO that has received enough confirmations. We have 3 steps in the process of consolidating UTXOs: 1. Retrieve the max spendable amount for a specific wallet. 2. This will result in an amount that considers up to 250 UTXOs within the wallet (by default, from smallest to biggest). 3. Create a transaction with the amount provided. ```javascript theme={"system"} const getMaxSpendableAmount = async ( assetId: string, vaultAccountId: string, ): Promise => { try { const maxSpendableAmount = await fireblocks.vaults.getMaxSpendableAmount({ vaultAccountId, assetId, }); console.log( JSON.stringify(maxSpendableAmount.data.maxSpendableAmount, null, 2), ); return maxSpendableAmount.data; } catch (error) { console.error(error); } }; let transactionPayload = {}; const consolidateWallet = async ( assetId: string, vaultAccountId: string, treasuryVault: string, ): Promise => { const amount = await getMaxSpendableAmount(assetId, vaultAccountId); transactionPayload = { assetId, amount: amount?.maxSpendableAmount, source: { type: TransferPeerPathType.VaultAccount, id: vaultAccountId, }, destination: { type: TransferPeerPathType.VaultAccount, id: treasuryVault, }, }; console.log(JSON.stringify(transactionPayload, null, 2)); try { const result = await fireblocks.transactions.createTransaction({ transactionRequest: transactionPayload, }); console.log(JSON.stringify(result.data, null, 2)); return result.data; } catch (error) { console.error(error); } }; getMaxSpendableAmount("BTC", "2"); consolidateWallet("BTC", "2", "0"); ``` ```javascript theme={"system"} async function consolidate(vaultAccountId, assetId, treasuryVault){ let amountToConsolidate = await fireblocks.getMaxSpendableAmount(vaultAccountId, assetId).maxSpendableAmount const payload = { assetId, amount: amountToConsolidate.maxSpendableAmount, source: { type: PeerType.VAULT_ACCOUNT, id: vaultAccountId }, destination: { type: PeerType.VAULT_ACCOUNT, id: treasuryVault }, }; const result = await fireblocks.createTransaction(payload); console.log(JSON.stringify(result, null, 2)); } consolidate("0", "BTC", "1"); ``` ```python theme={"system"} from fireblocks_sdk import TransferPeerPath, DestinationTransferPeerPath def consolidate(vault_id: str, asset: str, treasury_id: str): amountToConsolidate = fireblocks.get_max_spendable_amount(vault_id, asset)["maxSpendableAmount"] fireblocks.create_transaction( asset_id=asset, amount=amountToConsolidate, source=TransferPeerPath(VAULT_ACCOUNT, vault_id), destination=DestinationTransferPeerPath(VAULT_ACCOUNT, treasury_id) ) consolidate("0", "BTC", "1"); ``` Retrieving the maximum spendable amount for consolidation is used through the getMaxSpendableAmount endpoint. Do note you need the `vaultId` and `assetId`. Once we create a transaction with the specified amount, Fireblocks automatically selects all of the inputs (smallest to biggest) to consolidate. You can [contact Fireblocks Support](https://support.fireblocks.io/hc/en-us/requests/new) in order to change the default consolidation selection method and reverse it to a selection going from largest to smallest. > [!TIP] > > **UTXO consolidation to source** > > You can send a transaction to yourself, thus just consolidating the UTXOs without sending them outwards. # Contract Objects Source: https://developers.fireblocks.com/reference/contract-objects ## ContractAsset | Parameter | Type | Description | | - | - | - | | id | string | The ID of the contract wallet. | | balance | string | The balance of the contract wallet. | | lockedAmount | string | Locked amount in the contract wallet. | | status | ConfigChangeRequestStatus | Status of the contract wallet. | | activationTime | string | The time the contract wallet will be activated in case wallet activation is postponed according to the workspace definition. | | address | string | The address of the contract wallet. | | tag | string | Used as destination tag for XRP; `memo` for ATOM, EOS, HBAR, LUNA, LUNC, XDB, XEM; `memo_text` for XLM; `notes` for ALGO. | *** ## UnmanagedContract | Parameter | Type | Description | | - | - | - | | id | string | The ID of the unmanaged contract wallet. | | name | string | Name of the contract wallet container. | | assets | Array of [ContractAsset](#contractasset) objects | An array of assets available in the unmanaged contract wallet. | # Create Omnibus Structure Source: https://developers.fireblocks.com/reference/create-omnibus-structure In an omnibus setup, three vault types work together: * **Intermediate vault accounts**: Assigned per end-client for deposits. Use the Fireblocks API to generate as many as needed. * **Omnibus deposits**: The central vault where end-client funds are swept and stored. * **Withdrawal pool**: Vault accounts containing funds allocated for end-client withdrawals. More than one may be required due to blockchain limitations. Group them into a [Wallet Pool](/docs/wallet-pools) to send withdrawals from all of them as one source. Follow the right structure for your use case in the [Create Direct Custody Wallets](/docs/create-direct-custody-wallets) article. ## Understanding Asset Types Handling your omnibus account correctly requires a clear understanding of the differences between UTXO and account-based assets. Due to the nature of UTXO-based blockchains, a transaction includes the source address for each end client, unlike account-based transactions which require an intermediary vault account per client. ## UTXO Based ### Structure * In the Omnibus Deposits vault account, you can assign each end client a deposit address (derived from the permanent wallet address of the UTXO asset). * When adding an address for an end client in the Omnibus Deposits vault account, use the [Create a New Deposit Address of an Asset in a Vault Account](/reference/createvaultaccountassetaddress) endpoint and use the `description` field in `createAddressRequest` to associate the end client's ID. The `customerRefId` parameter is the ID for AML providers to associate the owner of funds with transactions. Both the name of the vault account and the AML `customerRefId` fields are propagated to every transaction. ### Deposit Funds are deposited using the following process: * The retail platform shares the deposit address with the end client. * The end client makes a deposit. * The incoming deposit triggers a webhook notification. * Your client-facing software automatically notifies the end client that the deposit was received. * The deposit appears on the Transaction History page. ### Example ```typescript TypeScript theme={"system"} const createUTXOWithdrawalVaultAccounts = async ( assetId: string, name: string, ): Promise | undefined> => { const result: Array<{}> = []; try { const vaultAccount = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name, }, }); if (vaultAccount.data) { const vaultWallet = await fireblocks.vaults.createVaultAccountAsset({ vaultAccountId: vaultAccount.data.id as string, assetId, }); result.push({ "Vault Account Name": vaultAccount.data.name, "Vault Account ID": vaultAccount.data.id, "Asset ID": assetId, Address: vaultWallet.data.address, }); console.log(JSON.stringify(result, null, 2)); } return result; } catch (error) { console.error(error); } }; // Create an omnibus vault account for UTXO based assets const createOmnibusUTXOAccount = async ( numOfAddresses: number, assetId: string, ): Promise<{} | undefined> => { try { const myOmnibusVault = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name: "My Omnibus Vault", }, }); if (myOmnibusVault.data) { const vaultAccountId = myOmnibusVault.data.id as string; let result = {}; await fireblocks.vaults.createVaultAccountAsset({ vaultAccountId, assetId, }); for (let i = 0; i < numOfAddresses; i++) { // Generating additional addresses is possible for UTXO based assets only await fireblocks.vaults.createVaultAccountAssetAddress({ assetId, vaultAccountId, createAddressRequest: { description: `UserAddress${i + 1}`, }, }); } const addresses = await fireblocks.vaults.getVaultAccountAssetAddressesPaginated({ vaultAccountId, assetId, }); result = { "Vault Account Name": myOmnibusVault.data.name, "VaultAccount ID": myOmnibusVault.data.id, "Asset ID": assetId, Addresses: addresses?.data.addresses, }; console.log(JSON.stringify(result, null, 2)); return result; } } catch (error) { console.error(error); } }; createUTXOWithdrawalVaultAccounts("BTC_TEST", "MyWithdrawalVault"); createOmnibusUTXOAccount(3, "BTC_TEST"); ``` ```javascript JavaScript theme={"system"} // Obtain a list of user identifiers associated with the vault accounts and pass them as strings inside internalCustRefIds // each of the internalCustRefIds is concatenated to the vault's name const internalCustRefIds = ["a","b","c"]; const assetId = "BTC_TEST"; async function createUTXOWithdrawalVaultAccounts(assetId, name){ vault = await fireblocks.createVaultAccount(name); vaultWallet = await fireblocks.createVaultAsset(Number(vault.id), assetId); const result = [{"Vault Name": vault.name, "Vault ID": vault.id, "Asset ID": assetId, "Wallet Address": vaultWallet.address}]; console.log(JSON.stringify(result, null, 2)); return(result); } async function createUTXOOmnibusAccount(amountOfVaultAccounts, assetId, internalCustRefIds){ let vault; let vaultWallet; let address = []; vault = await fireblocks.createVaultAccount("Omnibus"); vaultWallet = await fireblocks.createVaultAsset(Number(vault.id), assetId); for (let i = 0; i < amountOfVaultAccounts; i++){ address[i] = await fireblocks.generateNewAddress(Number(vault.id), assetId, "CustomerID_"+internalCustRefIds[i]+"_vault"); } console.log("Created vault account:"+JSON.stringify(vault, null, 2)+" with wallet addresses:"+JSON.stringify(address, null, 2)); return("Omnibus:", vault, "Addresses:", address); } createUTXOWithdrawalVaultAccounts(assetId, "Withdrawal"); createUTXOOmnibusAccount(2, assetId, internalCustRefIds); ``` ```python Python theme={"system"} # Obtain a list of user identifiers associated with the vault accounts and pass them as strings inside internalCustRefIds # each of the internalCustRefIds is concatenated to the vault's name ASSET = "BTC_TEST" CUSTOMER_IDS = ["a", "b", "c"] def create_utxo_withdrawal_vault(asset: str, name: str): vault_id = fireblocks.create_vault_account(name=name)["id"] address = fireblocks.create_vault_asset(vault_account_id=vault_id, asset_id=asset)["address"] return {name: vault_id}, address def create_utxo_omnibus_vault(amount: int, asset: str, customer_ids: list, hidden_on_ui: bool = True): deposit_address = {} vault_id = fireblocks.create_vault_account(name="Omnibus")["id"] fireblocks.create_vault_asset(vault_account_id=vault_id, asset_id=asset) for i in range(amount): address = fireblocks.generate_new_address(vault_account_id=vault_id, asset_id=asset, description=customer_ids[i], hidden_on_ui=hidden_on_ui)["address"] deposit_address[customer_ids[i]] = address return {"Omnibus": vault_id, "Addresses": deposit_address} print(create_utxo_withdrawal_vault(ASSET, "Withdrawal")) print(create_utxo_omnibus_vault(3, ASSET, CUSTOMER_IDS)) ``` This creates an omnibus vault and a withdrawal vault, then generates a deposit address per end user using a unique customer ID. The function returns a dictionary of the newly created vaults and generated deposit addresses. ## Account Based This section covers account-based assets without a tag or memo. For tag/memo-based assets, see [Tag / Memo Based](#tag-/-memo-based). ### Structure * The workspace should contain one or more intermediate vault accounts per end client, plus a single Omnibus Deposits vault account. * When adding a vault account, use the [Create a New Vault Account](/reference/createvaultaccount) endpoint and use the `name` parameter to associate the end client's ID as a prefix or suffix. * To create intermediate vault accounts in bulk with a tag already attached, for example `deposit`, use the bulk creation endpoint with `tagIds`, then filter your vault accounts by that tag. See [Tag vault accounts at creation](/docs/tags#tag-vault-accounts-at-creation). The `customerRefId` parameter is the ID for AML providers to associate the owner of funds with transactions. Both the name of the vault account and the AML `customerRefId` fields are propagated to every transaction. * Due to the nature of account-based blockchains, transactions can only be transferred from one account-based address to another sequentially, unlike UTXO where multiple addresses are included in a single transaction. ### Deposit Funds are deposited using the following process: * The end client receives a deposit address. * The end client makes a deposit. * The incoming deposit triggers a webhook notification. * Your client-facing software automatically notifies the end client that the deposit was received. * The deposit is swept to the Omnibus Deposits vault account. See [Sweeping within an Omnibus Vault structure](/docs/sweep-funds) for the sweeping logic. ### Example > [!TIP] > > **Recommended: Set `hiddenOnUI: true` for end-user vaults** > > When creating end-user vault accounts, set `hiddenOnUI: true` in the `createVaultAccount` request. By default it is `false`, which means all vaults will appear in the Console — not recommended at scale. > > Transfers to hidden vaults are only visible programmatically, not in the Console UI. The example below creates: 1. 5 intermediate vault accounts for 5 end users. 2. 1 treasury vault. 3. 3 withdrawal vaults to distribute settlement load. ```typescript TypeScript theme={"system"} const createAccountBasedVaultAccounts = async ( vaultAccountNamePrefix: string, numOfVaultAccounts: number, assetId: string, hiddenOnUI: boolean, endUserReferences?: string[], ): Promise | undefined> => { try { let vaultAccount: FireblocksResponse; let results: Array<{}> = []; for (let i = 0; i < numOfVaultAccounts; i++) { if ( endUserReferences && endUserReferences.length !== numOfVaultAccounts ) { throw new Error( "Number of Vault Accounts does not equal to the number of end user references", ); } vaultAccount = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name: endUserReferences ? vaultAccountNamePrefix + "_" + endUserReferences[i] : vaultAccountNamePrefix + "_Vault" + String(i + 1), hiddenOnUI, }, }); const vaultAccountId = vaultAccount.data?.id as string; const vaultWallet = await fireblocks.vaults.createVaultAccountAsset({ assetId, vaultAccountId, }); const singleVaultResult = { "Vault Account": vaultAccount.data?.name, "Vault Account ID": vaultAccountId, "Asset ID": assetId, Address: vaultWallet.data?.address, }; results.push(singleVaultResult); console.log( `Created Vault Account:\n ${JSON.stringify(singleVaultResult, null, 2)}`, ); } return results; } catch (error) { console.error(error); } }; createAccountBasedVaultAccounts("Deposits", 5, "ETH_TEST5", true, [ "UserA", "UserB", "UserC", "UserD", "UserE", ]); createAccountBasedVaultAccounts("Treasury", 1, "ETH_TEST5", false); createAccountBasedVaultAccounts("Withdrawal_Pool", 3, "ETH_TEST5", false); ``` ```javascript JavaScript theme={"system"} // Obtain a list of user identifiers associated with the vault accounts and pass them as strings inside internalCustRefIds // each of the internalCustRefIds is concatenated to the vault's name const internalCustRefIds = ["a","b","c","d","e"]; const assetId = "ETH_TEST3"; async function createAccountBasedVaultAccounts(vaultAccountNamePrefix, amountOfVaultAccounts, assetId, hiddenOnUI, internalCustRefIds){ let createVaultRes; let vault; let vaultWallet; for (let i = 0; i < amountOfVaultAccounts; i++){ if (internalCustRefIds){ createVaultRes = await fireblocks.createVaultAccount(vaultAccountNamePrefix.toString()+"_"+internalCustRefIds[i]+"_vault", hiddenOnUI); } else { createVaultRes = await fireblocks.createVaultAccount(vaultAccountNamePrefix.toString()+"_"+i.toString()+"_vault", hiddenOnUI); } vault = { vaultName: createVaultRes.name, vaultID: createVaultRes.id } vaultWallet = await fireblocks.createVaultAsset(Number(vault.vaultID), assetId); console.log("Created vault account", vault.vaultName,":", "with wallet address:", vaultWallet.address); } } createAccountBasedVaultAccounts("Deposits_End_User", 5, assetId, false, internalCustRefIds); createAccountBasedVaultAccounts("Treasury", 1, assetId, false, undefined); createAccountBasedVaultAccounts("Withdrawal_pool", 3, assetId, false, undefined); ``` ```python Python theme={"system"} # Obtain a list of user identifiers associated with the vault accounts and pass them as strings inside internalCustRefIds # each of the internalCustRefIds is concatenated to the vault's name CUSTOMER_IDS = ["a", "b", "c", "d", "e"] ASSET = "ETH_TEST3" def create_account_vault_accounts(prefix: str, amount: int, asset_id: str, customer_ids: list, is_hidden: bool = False) -> dict: vault_dict = {} for index in range(amount): if customer_ids: vault_name = f"{prefix}_{customer_ids[index]}_vault" else: vault_name = f"{prefix}_vault" vault_id = fireblocks.create_vault_account(name=vault_name, hidden_on_ui=is_hidden)["id"] fireblocks.create_vault_asset(vault_id, ) vault_dict[vault_name] = vault_id return vault_dict print(create_account_vault_accounts("End-User", 5, ASSET, CUSTOMER_IDS, True)) print(create_account_vault_accounts("Treasury", 1, ASSET)) print(create_account_vault_accounts("Withdrawal", 3, ASSET)) ``` The function accepts a name prefix, account count, and optionally `internalCustRefIds` and `hiddenOnUI`. It runs three times: once for end-user vaults, once for the treasury vault, and once for withdrawal vaults. ## Tag / Memo Based Tag and memo-based assets share a single wallet address per omnibus vault, with a unique tag or memo identifying each end client. The tag/memo name varies by blockchain. ### Structure * In the Omnibus Deposits vault account, you can assign each end client a tag or memo. * When adding an address for an end client in the Omnibus Deposits vault account, use the [Create a New Deposit Address of an Asset in a Vault Account](/reference/createvaultaccountassetaddress) endpoint and use the `description` field in `createAddressRequest` to associate the end client's ID. The `customerRefId` parameter is the ID for AML providers to associate the owner of funds with transactions. Both the name of the vault account and the AML `customerRefId` fields are propagated to every transaction. ### Deposit Funds are deposited using the following process: * The end client receives a deposit address and a tag or memo. * The end client makes a deposit using both the address and the tag. * The incoming deposit triggers a webhook notification. * Your client-facing software automatically notifies the end client that the deposit was received, provided they included the tag or memo. * All funds are held in the same vault account; different customer balances are tracked in your internal ledger. ### Example ```typescript TypeScript theme={"system"} const createTagWithdrawalVaultAccounts = async ( assetId: string, name: string, ): Promise | undefined> => { const result: Array<{}> = []; try { const vaultAccount = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name, }, }); if (vaultAccount.data) { const vaultWallet = await fireblocks.vaults.createVaultAccountAsset({ vaultAccountId: vaultAccount.data.id as string, assetId, }); result.push({ "Vault Account Name": vaultAccount.data.name, "Vault Account ID": vaultAccount.data.id, "Asset ID": assetId, Address: vaultWallet.data.address, }); console.log(JSON.stringify(result, null, 2)); } return result; } catch (error) { console.error(error); } }; // Create an omnibus vault account for Tag/Memo based assets const createTagOmnibusAccount = async ( numOfAddresses: number, assetId: string, ): Promise<{} | undefined> => { try { const myOmnibusVault = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name: "My Omnibus Vault", }, }); if (myOmnibusVault.data) { const vaultAccountId = myOmnibusVault.data.id as string; let result = {}; await fireblocks.vaults.createVaultAccountAsset({ vaultAccountId, assetId, }); for (let i = 0; i < numOfAddresses; i++) { // For Tag/Memo based assets, the address of the wallet is always the same but a new Memo/Tag is generated upon each user await fireblocks.vaults.createVaultAccountAssetAddress({ assetId, vaultAccountId, createAddressRequest: { description: `UserAddress${i + 1}`, }, }); } const addresses = await fireblocks.vaults.getVaultAccountAssetAddressesPaginated({ vaultAccountId, assetId, }); result = { "Vault Account Name": myOmnibusVault.data.name, "VaultAccount ID": myOmnibusVault.data.id, "Asset ID": assetId, Addresses: addresses?.data.addresses, }; console.log(JSON.stringify(result, null, 2)); return result; } } catch (error) { console.error(error); } }; createTagWithdrawalVaultAccounts("XLM_TEST", "Withdrawal"); createTagOmnibusAccount(2, "XLM_TEST"); ``` ```javascript JavaScript theme={"system"} // Obtain a list of user identifiers associated with the vault accounts and pass them as strings inside internalCustRefIds // each of the internalCustRefIds is concatenated to the vault's name const internalCustRefIds = ["a","b","c"]; const assetId = "XLM_TEST"; async function createTagWithdrawalVaultAccounts(assetId, name){ vault = await fireblocks.createVaultAccount(name); vaultWallet = await fireblocks.createVaultAsset(Number(vault.id), assetId); const result = [{"Vault Name": vault.name, "Vault ID": vault.id, "Asset ID": assetId, "Wallet Address": vaultWallet.address}]; console.log(JSON.stringify(result, null, 2)); return(result); } async function createTagOmnibusAccount(amountOfVaultAccounts, assetId, internalCustRefIds){ let vault; let vaultWallet; let tag = []; vault = await fireblocks.createVaultAccount("Omnibus"); vaultWallet = await fireblocks.createVaultAsset(Number(vault.id), assetId); for (let i = 0; i < amountOfVaultAccounts; i++){ tag[i] = await fireblocks.generateNewAddress(Number(vault.id), assetId, "CustomerID_"+internalCustRefIds[i]+"_vault"); } console.log("Created vault account:"+JSON.stringify(vault, null, 2)+" with wallet tag:"+JSON.stringify(tag, null, 2)); return("Omnibus:", vault, "Tags:", tag); } createTagWithdrawalVaultAccounts(assetId, "Withdrawal"); createTagOmnibusAccount(2, assetId, internalCustRefIds); ``` ```python Python theme={"system"} ASSET = "XLM_TEST" CUSTOMER_IDS = ["a", "b", "c"] def create_tag_withdrawal_vault(asset: str, name: str): vault_id = fireblocks.create_vault_account(name=name)["id"] address = fireblocks.create_vault_asset(vault_account_id=vault_id, asset_id=asset)["address"] return {name: vault_id}, address def create_tag_omnibus_vault(amount: int, asset: str, customer_ids: list, hidden_on_ui: bool = True): deposit_tags = {} vault_id = fireblocks.create_vault_account(name="Omnibus") address = fireblocks.create_vault_asset(vault_account_id=vault_id, asset_id=asset)["address"] for i in range(amount): tag = fireblocks.generate_new_address(vault_account_id=vault_id, asset_id=asset, description=customer_ids[i], hidden_on_ui=hidden_on_ui)["tag"] deposit_tags[customer_ids[i]] = address return {"Omnibus": vault_id, "Address": address, "Tags": deposit_tags} print(create_tag_withdrawal_vault(ASSET, "Withdrawal")) print(create_tag_omnibus_vault(3, ASSET, CUSTOMER_IDS)) ``` This creates an omnibus vault and a withdrawal vault, then generates a unique deposit identifier per end user. The function returns a dictionary of vault IDs and deposit address mappings. # Create a Staking Position Source: https://developers.fireblocks.com/reference/create-stake 1. [Approve the terms of service](/reference/approvetermsofservicebyproviderid) of your desired staking provider (Kiln or Figment): ```javascript theme={"system"} const FireblocksSDK = require("fireblocks-sdk").FireblocksSDK; const fireblocks = new FireblocksSDK(privateKey, apiKey); const approveRequest = await fireblocks.approveStakingProviderTermsOfService("kiln"); ``` 2. [Create your staking position](/reference/stake) : ```javascript theme={"system"} const stakeRequest = await fireblocks.executeStakingStake( "SOL",{ vaultAccountId:"1", providerId:"kiln", stakeAmount:"100", txNote:"stake 100 SOL from vaultId 1, 24 Jan 2024" } ); ``` **Important:** The response object from the staking action includes an ID that serves as the unique identifier for the staking position. It's essential to save this ID for future reference. If you lose this ID, you can retrieve all the IDs from the staking position in your workspace by using the `fireblocks.getStakingPositions()` function. ## Monitor and sign the staking request [Find the relevant transaction](/reference/getdelegationbyid) - After creating the staking position, find the new transaction ID using `getStakingPosition(id)`. The transaction ID can be found under `responseBody.relatedTransactions[0].txId`: ```javascript theme={"system"} const positionId = stakeRequest.id const position = await fireblocks.getStakingPosition(positionId); // When creating a new staking position, there is only 1 transaction const txId = position.relatedTransactions[0].txId // ... sign it ``` To sign the transaction, please follow the necessary steps outlined. Learn more about the [transaction signing request and approval process](https://support.fireblocks.io/hc/en-us/articles/12006018592156-API-Co-Signer-Overview). Once the stake position is created, you can monitor the position status and available actions, such as unstaking or withdrawing, using `getPosition(positionId)`. # Create Vault Accounts Source: https://developers.fireblocks.com/reference/create-vault-account Your Fireblocks workspace utilizes a hierarchy of vault accounts as part of its [Object Model](/docs/object-model). This hierarchy allows you to segregate funds belonging to different business units or deposited by different users. A Vault account can hold multiple assets. To transfer to or from vault accounts, your source or destination targets must be provided with both their vault ID and asset ID. Use the [Create a new vault account](/reference/createvaultaccount) API call to create a new vault account that will hold your UTXO or account-based wallets. ### Important: Vault account names should consist of ASCII characters only. ### Code Example ```javascript theme={"system"} (async() => { const vaultAccount = await fireblocks.vaults.createVaultAccount({ createVaultAccountRequest: { name: "MyVaultAccount", autoFuel: true, hiddenOnUI: false } }) console.log(JSON.stringify(vaultAccount.data, null, 2)) })(); ``` ```python theme={"system"} vault_account = fireblocks.create_vault_account( name="MyVaultAccount", hiddenOnUI=False, autoFuel=True ) ``` ### Response Example ```json theme={"system"} { "id": "148", "name": "MyVaultAccount", "hiddenOnUI": false, "assets": [], "autoFuel": true } ``` *** > [!TIP] > > **Create tagged vault accounts in bulk.** This endpoint does not accept tags. To create many vault accounts at once with [tags](/docs/tags) already attached, use the bulk creation endpoint with `tagIds`. See [Tag vault accounts at creation](/docs/tags#tag-vault-accounts-at-creation). # Optional parameters ### hiddenOnUI Set to `true` by default, hides this vault account from appearing in the Fireblocks Console. This is the best practice when creating intermediate deposit vault accounts for your users as it helps reduce visual clutter and improves UI loading time. The best practice is configuring this setting so that only your omnibus account and another operational vault account (or multiple) are visible in the Fireblocks Console. > [!NOTE] > > **Hiding is a Console-only view change.** A hidden vault account still exists and remains fully functional: it retains its ID, keeps all wallets and balances, is reachable through the API, and continues to emit webhook events (for example, `TRANSACTION_STATUS_UPDATED` and `VAULT_BALANCE_UPDATE`). To toggle visibility later, call [Hide a vault account](/api-reference/vaults/hide-a-vault-account-in-the-console) or [Unhide a vault account](/api-reference/vaults/unhide-a-vault-account-in-the-console), or see [Manage vault accounts](https://support.fireblocks.io/hc/en-us/articles/26075380927132-Manage-vault-accounts) for the Console walkthrough. ### Deleting or archiving vault accounts Vault accounts are permanent. Fireblocks does not expose an API or Console action to delete or archive a vault account, and there is no `archive` equivalent to `hiddenOnUI`. To remove a vault account from day-to-day views, hide it from the Console; the underlying account, addresses, and history are preserved. ### autoFuel If the Gas Station service is enabled on your workspace, this flag needs to be set to `true` if you wish to monitor and fuel this account's Ethereum address upon detected balance refresh events, such as when deposits of ERC20 tokens occur. # Create Vault Wallets Source: https://developers.fireblocks.com/reference/create-vault-wallet Fireblocks vault accounts contain vault wallets. For each vault wallet, there are one or more deposit addresses. # Address generation ## Account-based wallets For account-based assets, there are two address-generation options: 1. Account-based assets *without* tag/memo/note support, such as ETH, can only generate one deposit address, making each vault account unique per vault wallet. This allows you to manage one deposit address per asset, per vault account. This requires you to create additional vault accounts when more than one deposit address of the same asset is required. 2. Account-based assets *with* tag/memo/note support, such as XRP, can generate one or more deposit addresses. Each deposit address has the same on-chain address. However, they are differentiated by their tag/memo. ## UTXO wallets UTXO-based assets, such as Bitcoin, can hold multiple wallet addresses. This allows you to manage deposits in a single vault account. ## Hedera HTS tokens When you enable a new Hedera Token Service (HTS) token in a new vault, select one of the following funding options: 1. **First sender covers the fee:** The storage fee is deducted when the first incoming transaction occurs. No upfront action is required. 2. **Fund from the vault HBAR balance:** The storage fee is deducted from the vault's HBAR balance immediately after wallet creation. This option requires transaction signing. Set `blockchainWalletType` to `"Unassociated"` for the first option and `"Associated"` for the second option. > [!NOTE] > > HBAR wallets created before July 2026 do not support the first sender option. For these wallets, use the **Fund from the vault HBAR balance** option to associate each HTS token before receiving deposits. Deposits to an account that is not associated with the token fail on-chain with the `TOKEN_NOT_ASSOCIATED_TO_ACCOUNT` error. For details on how HTS token association compares to other blockchains, see [Create Direct Custody Wallets](/docs/create-direct-custody-wallets#hedera). ### Create Vault Wallet code example: ```javascript theme={"system"} (async() => { try { const vaultWallet = await fireblocks.vaults.createVaultAccountAsset({ vaultAccountId: "148", assetId: "BTC" }) console.log(JSON.stringify(vaultWallet, null, 2)) } catch(e){ console.log(e) } })(); ``` ```python theme={"system"} vault_wallet = fireblocks.create_vault_asset( vault_account_id="148", asset_id="BTC" ) pprint.pprint(vault_wallet) ``` ### Response example ```json theme={"system"} { 'address': 'bc1q8q87uy8h2mru0q5gc7u680w76dz350s6sx5u8a', 'id': '148', 'legacyAddress': '167RvaKttzN2wRrJwmYEwjKG7kvdjwhKfq', 'tag': '' } ``` ### Create Deposit Address for UTXO and Tag/Memo based assets: ```javascript theme={"system"} (async() => { try { const depositAddress = await fireblocks.vaults.createVaultAccountAssetAddress({ assetId: "BTC", vaultAccountId: "148", createAddressRequest: { description: "EndUserA" } }) console.log(JSON.stringify(depositAddress, null, 2)) } catch(e){ console.log(e) } })(); ``` ```python theme={"system"} deposit_address= fireblocks.generate_new_address( vault_account_id='148', asset_id='BTC', description='EndUserA' ) pprint.pprint(deposit_address) ``` ### Response Example: ```json theme={"system"} { 'address': 'bc1qkwxyzs68a3nsulhe4eavfm5dzuucf25jjnefex', 'bip44AddressIndex': 1, 'legacyAddress': '1HNMyRRzt4beCDnYLRwQKEbPAwTgmKjwrJ', 'tag': '' } ``` # Create Workflow Configuration Source: https://developers.fireblocks.com/reference/create-workflow-configuration Call the [POST /payments/workflow\_config](/reference/createflowconfiguration) endpoint. After a configuration is created, the operation type for the WC can’t be changed. However, other WC parameters can be defined and edited later when creating the WE. ## Example ```json theme={"system"} { configName: 'some-name', preScreening: { enabled: true }, failureHandling: { enabled: true, type: 'REVERSE' }, configOperations: { type: 'TRANSFER', params: { source: { accountId: 'xyz', accountType: 'EXCHANGE_ACCOUNT', }, assetId: 'USDC_POLYGON', destination: { accountType: 'VAULT_ACCOUNT', accountId: '0', }, }, }, { type: 'TRANSFER', params: { destination: { accountType: 'EXCHANGE_ACCOUNT', accountId: 'abc', }, source: { accountType: 'VAULT_ACCOUNT', accountId: '0', }, assetId: 'USDC_POLYGON', }, }, } ``` This route returns the configuration ID which will be used to create the WE. A configuration proceeds through the following statuses during its creation. These statuses will be applied to each WCO and the WC as a whole. 1. **PENDING:** The configuration is about to start the validation process. 2. **VALIDATION\_IN\_PROGRESS:** The validation process has started, ensuring the configuration is valid. 3. **VALIDATION\_FAILED** or **READY\_FOR\_EXECUTION:** The validation failed or the configuration can be executed. These statuses are finite states. All the operation validation processes must succeed for the configuration validation to succeed. If one or more operation validations fail, the entire configuration validation fails. Failed WCOs may show VALIDATION\_FAILED while others may show VALIDATION\_COMPLETED. However, the WC status will show VALIDATION\_FAILED. The failure details will be included under the failed operation in the validationFailure field. In this scenario, you should call the API endpoint again with the corrected configuration. # Create Workflow Execution Source: https://developers.fireblocks.com/reference/create-workflow-execution Once the WC is in **READY\_FOR\_EXECUTION** status, call the [POST /payments/workflow\_execution](/reference/createflowexecution) endpoint. When creating a WE, mandatory parameters, such as amounts and addresses, that weren’t provided during the configuration phase must be provided. Parameters provided when creating the WC can be overridden when creating the WE. Some parameters are only provided when creating the WE. The `amount` parameter can only be provided during the WE since it’s required for the preview and launch but not for the WC, which is just a template. This parameter is usually provided to the first operation and then carried from one operation to another down the flow. ## Example ```json theme={"system"} { "configId": "string", "preScreening":{ "enabled": false }, failureHandling: { enabled: true, type: 'REVERSE' }, "params": [ { "configOperationId": "string", "configOverrideParams": { "accountId": "string", "srcAssetId": "string", "destAssetId": "string", "slippageBasisPoints": 10000 }, "executionParams": { "amount": "string" } }, { "configOperationId": "string", "configOverrideParams": { "paymentAccount": { "accountId": "string", "accountType": "EXCHANGE_ACCOUNT" }, "instructionSet": [ { "payeeAccount": { "accountId": "string", "accountType": "EXCHANGE_ACCOUNT" }, "assetId": "string", "amount": "string" }, { "payeeAccount": { "accountId": "string", "accountType": "EXCHANGE_ACCOUNT" }, "assetId": "string", "percentage": "string" } ] }, "executionParams": { "amount": "string" } } ] } ``` The request body `configId` field must include the id for a WC in READY\_FOR\_EXECUTION status. The request body `params` field holds an array. Each item of the array has a `configOperationId`, which points to a specific operation from the WC. This is necessary when overriding or setting an operation. An execution proceeds through the following statuses during its creation: 1. **PENDING:** The execution is about to start the validation process. 2. **VALIDATION\_IN\_PROGRESS:** The validation process has started, ensuring the execution is valid. 3. **VALIDATION\_FAILED** or **VALIDATION\_COMPLETED:** The validation failed or completed. If completed, the preview process starts automatically. 4. **PREVIEW\_IN\_PROGRESS:** The preview process has started. 5. **PREVIEW\_FAILED** or **READY\_FOR\_LAUNCH:** The preview failed or the execution can be launched. These statuses are finite states. If an execution is in VALIDATION\_FAILED status or a PREVIEW\_FAILED, it can't be launched or used again. To get another corrected preview, create a new WE to trigger the validation and preview processes. # Data Objects Source: https://developers.fireblocks.com/reference/data-objects On the following pages, you can find descriptions of all the data objects expected and returned by the Fireblocks API's endpoints. # Deploy an NFT Collection Source: https://developers.fireblocks.com/reference/deploy-an-nft-collection You can deploy smart contracts using [The Fireblocks Hardhat plugin integration](https://hardhat.org/hardhat-runner/docs/guides/deploying) # Create your ERC-721 collection You can create your own NFT collection using the common ERC-721 standard. For this example, we’ll name our collection “Space Bunnies”. There are some great tools to help you do this pretty easily. This example was pre-generated using the [Contracts Wizard by OpenZeppelin](https://docs.openzeppelin.com/contracts/5.x/wizard). ```solidity theme={"system"} pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract SpaceBunnies is ERC721, ERC721URIStorage, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("Space Bunnies", "SPCB") {} function safeMint(address to, string memory uri) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } // The following functions are overrides required by Solidity. function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { super._burn(tokenId); } function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); } } ``` > [!NOTE] > > **OpenZeppelin required** > > To run the generated code, you also need to install the OpenZeppelin contracts package from NPM. > > In your project directory, run: `npm install @openzeppelin/contracts`. You are ready to go! Check out the [Fireblocks Hardhat Plugin Documentation](/reference/hardhat-plugin), [Fireblocks Local JSON RPC](/reference/evm-local-json-rpc) and [Fireblocks Web3 provider](/reference/evm-web3-provider). Make sure you go through these and deploy the above contract as explained there, using your choice of available options. > [!TIP] > > **Hardhat Deployment** > > Hardhat is the go-to market solution for easily deploying contracts. We highly recommend that you choose this option if you feel comfortable with going through the configuration process. # Estimate Transaction Fee Source: https://developers.fireblocks.com/reference/estimate-transaction-fee > [!NOTE] > > Learn more about Fee Management in Fireblocks in [the following guide](/docs/verify-fee-effeciency) Ethereum (ETH) fees, also referred to as "gas price" or "gas fees," are calculated based on the amount of network activity and the network resources required. Fireblocks allows you to set the fee when creating a new transaction. You can either use a **custom fee** to set the amount used manually, or you can have Fireblocks estimate the fee by default. You can estimate network gas fees using the Fireblocks API: * Estimate a tentative fee for low, medium, or high rates by making a request to [Estimate the required fee for an asset.](/reference/estimatenetworkfee) * Estimate a specific fee based on your transaction's parameters by making a request to [Estimate the transaction fee.](/reference/estimatetransactionfee) > [!NOTE] > > **Fee levels determine transaction speed** > > The fee rate dictates the speed at which your transaction will be picked up and confirmed by the blockchain. When setting a tentative fee rate, the fee amount is an estimate only. Gas fee calculation is not finalized until your transaction is signed. > [!NOTE] > > **Using custom fee** > > Using the custom fee option can be complex as it offers different parameters, similar to stock trading, that allow you to set options like: > > * **Maximum priority fee** - Transaction time takes priority over gas fee price. > * **Max fee** - The gas fee max price is preset and takes priority over transaction time. > > [Learn more about the current Ethereum Network EIP-1559 standard for fee estimation.](https://support.fireblocks.io/hc/en-us/articles/4403728014994-London-Fork-and-EIP-1559) *** # Estimated network fee > [!NOTE] > > **Response Caching:** > > The network fee response is cached for 30 seconds on the Fireblocks side. Customers should consider this interval when using this endpoint, as querying it more frequently than every 30 seconds will not provide additional value. You'll see the tentative fee estimation in your fee values before creating your transaction data. This example shows how the estimated network fee is returned by the `getFeeForAsset` SDK function for the ETH asset. A high `priorityFee` estimation is used here to create a transaction sending 0.001 from vault ID `0` to vault ID `1`. The fee level can be either `LOW` / `MEDIUM` / `HIGH`. It will be `MEDIUM` by default. * **Note**: You are required to specify the `maxFee`when using `priorityFee`. ```javascript theme={"system"} const createTxWithNetworkFee = async ( payload: TransactionRequest, ): Promise => { try { const fee = await fireblocks.transactions.estimateNetworkFee({ assetId: payload.assetId as string, }); console.log( JSON.stringify( `Priority fee for the Tx is: ${fee.data.high.priorityFee}`, ), ); payload.priorityFee = fee.data.high.priorityFee; payload.maxFee = "120"; const result = await fireblocks.transactions.createTransaction({ transactionRequest: payload, }); console.log(JSON.stringify(result.data, null, 2)); return result.data; } catch (error: any) { console.error(error); } }; createTxWithNetworkFee({ assetId: "ETH", source: { type: TransferPeerPathType.VaultAccount, id: "0", }, destination: { type: TransferPeerPathType.VaultAccount, id: "1", }, amount: 0.01, }); ``` ```javascript theme={"system"} async function createTransactionWithNetworkFee(assetId, amount, sourceId, destId){ const fee = await getFee(assetId) console.log(JSON.stringify("Priority Fee for Transaction is: " + fee.high.priorityFee, null, 2)); const payload = { assetId, source: { type: PeerType.VAULT_ACCOUNT, id: sourceId }, destination: { type: PeerType.VAULT_ACCOUNT, id: destId }, amount, priorityFee: fee.high.priorityFee, maxFee: "120" }; const result = await fireblocks.createTransaction(payload); console.log(JSON.stringify(result, null, 2)); } createTransactionWithNetworkFee("ETH", "0.001", "0", "1"); ``` ```python theme={"system"} def create_transaction_with_network_fee(asset_id: str, amount: str, source_id: str, dest_id: str): fee = fireblocks.get_fee_for_asset(asset_id=asset_id) print("Priority Fee for Transaction is:", fee["high"]["priorityFee"]) result = fireblocks.create_transaction( asset_id=asset_id, source=TransferPeerPath( peer_type=VAULT_ACCOUNT, peer_id=source_id ), destination=DestinationTransferPeerPath( peer_type=VAULT_ACCOUNT, peer_id=dest_id ), amount=amount, priority_fee=fee["high"]["priorityFee"], max_fee="120" ) print(result) create_transaction_with_network_fee("ETH", "0.001", "0", "1") ``` *** # Estimate transaction fee You'll see the `feeLevel` parameter value inside your transaction data based on the fee level selection from the `estimateFeeForTransaction` SDK function. This example shows how the estimated network fee is returned by the `getFeeForAsset` SDK function for the ETH asset. A high `priorityFee` estimation is used here to create a transaction sending 0.001 from vault ID `0` to vault ID `1`: ```javascript theme={"system"} const getTxFee = async ( payload: TransactionRequest, ): Promise => { try { const txFee = await fireblocks.transactions.estimateTransactionFee({ transactionRequest: payload, }); return txFee.data; } catch (error: any) { console.error(error); } }; // The function should include the logic that will determine the fee level used // it should be based on the threshold relevant for your use-case // The logic options are HIGH/LOW as MEDIUM is the default when feeLevel is null const getFeeLevelToUse = ( feeEstimation: EstimatedTransactionFeeResponse, ): TransactionRequestFeeLevelEnum | undefined => { let feeLevelToUse: TransactionRequestFeeLevelEnum | undefined; if ("") { feeLevelToUse = TransactionRequestFeeLevelEnum.High; } else if ("") { feeLevelToUse = TransactionRequestFeeLevelEnum.Low; } return feeLevelToUse; }; const createTxWithFeeEstimation = async ( payload: TransactionRequest, ): Promise => { try { const txFee = (await getTxFee(payload)) as EstimatedTransactionFeeResponse; const feeLevel = getFeeLevelToUse(txFee); payload.feeLevel = feeLevel as TransactionRequestFeeLevelEnum; const result = await fireblocks.transactions.createTransaction({ transactionRequest: payload, }); console.log(JSON.stringify(result.data, null, 2)); return result.data; } catch (error: any) { console.error(error); } }; createTxWithFeeEstimation({ assetId: "ETH", source: { type: TransferPeerPathType.VaultAccount, id: "0", }, destination: { type: TransferPeerPathType.VaultAccount, id: "1", }, amount: 0.01, }); ``` ```javascript theme={"system"} async function getTxFee(payload){ const getTxFee = await fireblocks.estimateFeeForTransaction(payload); return getTxFee; } // the function should include the logic that will determine the fee level used // it should be based on the threshold relevant for your use-case // The logic options are HIGH/LOW as MEDIUM is the default when feeLevel is null function logicForWhatFeeLevelToUse(feeEstimation){ if(... <= myThreshold){ feeLevelToUse = FeeLevel.HIGH } else if(.....){ feeLevelToUse = FeeLevel.LOW } return feeLevelToUse; } async function createTransaction(assetId, amount, sourceId, destId){ var feeLevel; const payload = { assetId, amount, source: { type: PeerType.VAULT_ACCOUNT, id: sourceId }, destination: { type: PeerType.VAULT_ACCOUNT, id: destId }, feeLevel }; payload.feeLevel = logicForWhatFeeLevelToUse(getTxFee(payload)); const result = await fireblocks.createTransaction(payload); console.log(JSON.stringify(result, null, 2)); } createTransaction("ETH", "0.001", "0", "1"); ``` ```python theme={"system"} def create_transaction(asset_id: str, amount: str, source_id: str, dest_id: str): fee_level = "" payload = { "asset_id": asset_id, "amount": amount, "source": TransferPeerPath( peer_type=VAULT_ACCOUNT, peer_id=source_id ), "destination": DestinationTransferPeerPath( peer_type=VAULT_ACCOUNT, peer_id=dest_id ), "fee_level": fee_level } try: payload["fee_level"] = logic_for_what_fee_level_to_use(fireblocks.estimate_fee_for_transaction(**payload)) result = fireblocks.create_transaction(**payload) print(result) except Exception as e: print("Error:", e) ``` # EVM Local JSON RPC Source: https://developers.fireblocks.com/reference/evm-local-json-rpc The Fireblocks JSON-RPC is a command-line server that utilizes the Fireblocks API to facilitate interactions with Ethereum Virtual Machine (EVM) chains. It enables users to perform blockchain operations through a JSON-RPC interface, and it was developed to simplify the integration of Fireblocks' secure wallet infrastructure with blockchain applications. In this article, you will find installation instructions, code examples, and guidance on how to utilize Fireblocks JSON RPC and other frameworks (e.g. Brownie, Foundry) for smart contract development and interactions. # Getting Started ## web3.py integration [Fireblocks JSON-RPC](https://github.com/fireblocks/fireblocks-json-rpc) helps seamlessly integrate Fireblocks into your web3.py development stack. You can use it to deploy contracts, sign messages, and send transactions. ### Installation ```bash theme={"system"} npm install -g @fireblocks/fireblocks-json-rpc pip install web3 ``` ### Setup Set the [environment variables](https://github.com/fireblocks/fireblocks-json-rpc/blob/main/.env.example). Create `example.py`: ```python theme={"system"} import json import os from datetime import datetime from web3 import Web3 web3 = Web3(Web3.IPCProvider(os.environ['FIREBLOCKS_JSON_RPC_ADDRESS'], 60000*180)) web3.eth.defaultAccount = web3.eth.accounts[0] CONTRACT_ADDRESS = Web3.toChecksumAddress("0x8A470A36a1BDE8B18949599a061892f6B2c4fFAb") GREETER_ABI = json.loads('[{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"}]') GREETING = "Hello web3! By " + web3.eth.defaultAccount + " at " + str(datetime.now()) if __name__ == '__main__': print('last block number: ', web3.eth.blockNumber) for account in web3.eth.accounts: print('account: ', account) print('account balance: ', web3.fromWei(web3.eth.getBalance(account), "ether"), ' ETH\n') print('Greeter contract: https://sepolia.etherscan.io/address/' + CONTRACT_ADDRESS) contract = web3.eth.contract(address=CONTRACT_ADDRESS, abi=GREETER_ABI) print('Current greeting:', contract.functions.greet().call()) print('Setting greeting to:', GREETING) tx_hash = contract.functions.setGreeting(GREETING).transact({'from':web3.eth.defaultAccount}) print('Transaction signed and broadcasted: https://sepolia.etherscan.io/tx/' + tx_hash.hex()) print('Waiting for transaction to be mined...') web3.eth.wait_for_transaction_receipt(tx_hash) print('Current greeting:', contract.functions.greet().call()) ``` ### Usage ```bash theme={"system"} fireblocks-json-rpc -- python example.py ``` You can now use the `web3` object exactly as you normally would! # Ethereum Smart Contract Development ## Using Brownie Use the [Fireblocks JSON-RPC](https://github.com/fireblocks/fireblocks-json-rpc) to seamlessly work with Brownie on top of Fireblocks. ## Installation ### Step 1 - Brownie installation: Please follow the Brownie official [documentation](https://eth-brownie.readthedocs.io/en/stable/install.html) for installation ### Step 2 - Install Fireblocks JSON RPC server: ```bash theme={"system"} npm install -g @fireblocks/fireblocks-json-rpc ``` ## Setup ### Step 1 - Create a new example Brownie project: ```bash theme={"system"} brownie bake token cd token ``` ### Step 2 - set environment variables: Set the [environment variables](https://github.com/fireblocks/fireblocks-json-rpc/blob/main/.env.example) ### Step 3 - Add Fireblocks Sepolia network to Brownie: ```bash theme={"system"} brownie networks add Fireblocks fireblocks-sepolia name="Sepolia (Fireblocks)" chainid=11155111 host='http://127.0.0.1:8545/${FIREBLOCKS_API_KEY}' timeout=600 ``` ### Step 4 - Configure brownie-config.yaml file: Add ***dotenv: .env*** parameter to the config yaml file. Example: ```yaml theme={"system"} # exclude SafeMath when calculating test coverage # https://eth-brownie.readthedocs.io/en/v1.10.3/config.html#exclude_paths reports: exclude_contracts: - SafeMath dotenv: .env ``` ## Usage: ```bash theme={"system"} fireblocks-json-rpc -- brownie run --network fireblocks-sepolia scripts/token.py ``` ## Using Foundry ## Installing Foundry You can skip this section if you already have Foundry installed or follow that [Foundry Installation guide](https://book.getfoundry.sh/getting-started/installation) , [Foundry New Project guide](https://book.getfoundry.sh/projects/creating-a-new-project) on the Foundry website for all details. We provide you with a very basic process here for convenience: ```bash theme={"system"} curl -L https://foundry.paradigm.xyz | bash foundryup forge init hello_foundry cd hello_foundry forge build forge test ``` After you complete these steps, you'll have a new Foundry project. ## Foundry integration The [Fireblocks Local JSON-RPC](https://github.com/fireblocks/fireblocks-json-rpc) helps seamlessly integrate Fireblocks into your [Foundry](https://book.getfoundry.sh/) development stack. You can use it to deploy contracts, sign messages, and send transactions. ### Installation ```bash theme={"system"} npm install -g @fireblocks/fireblocks-json-rpc ``` ### Configuration Configuration can be set via command line flags or environment variables. Command line flags: * Ignore apiBaseUrl if you are not using a sandbox environment ```bash theme={"system"} fireblocks-json-rpc --apiKey --privateKey --chainId --apiBaseUrl https://sandbox-api.fireblocks.io ``` Environment variables: * Ignore FIREBLOCKS\_API\_BASE\_URL if you are not using a sandbox environment ```bash theme={"system"} FIREBLOCKS_API_KEY= \ FIREBLOCKS_API_PRIVATE_KEY_PATH= \ FIREBLOCKS_CHAIN_ID= \ FIREBLOCKS_API_BASE_URL=https://sandbox-api.fireblocks.io fireblocks-json-rpc ``` Delete the files that came with your Foundry boilerplate template: ```bash theme={"system"} rm test/Counter.t.sol script/Counter.s.sol src/Counter.sol ``` ## Deploy Contract - Using 'forge script' Now that you have a Foundry project set up, create your smart contract `src/Hello.sol` in the project folder and then deploy it to the Sepolia Ethereum Testnet. ### Step 1: Create and compile the Solidity file ```solidity theme={"system"} pragma solidity ^0.8.17; contract HelloWorld { string public greet = "Hello World!"; } ``` Run the following command to compile it: ```bash theme={"system"} forge build ``` ### Step 2: Create the deployment script Create a deployment script `script/Hello.s.sol` : ```solidity theme={"system"} pragma solidity ^0.8.17; import "forge-std/Script.sol"; import "../src/Hello.sol"; contract MyScript is Script { function run() external { vm.startBroadcast(); HelloWorld hello = new HelloWorld(); vm.stopBroadcast(); } } ``` ### Step 3: Deploy smart contract Deploy your contract to the Ethereum Sepolia Testnet (It is recommended to use the `--slow` parameter in your forge script command, specifically when the script is processing few transactions sequentially): ```bash theme={"system"} FIREBLOCKS_API_KEY=12345678-1234-1234-1234-123456789abc \ FIREBLOCKS_API_PRIVATE_KEY_PATH=/path/to/secret.key \ FIREBLOCKS_CHAIN_ID=11155111 \ fireblocks-json-rpc --http -- \ forge script script/Hello.s.sol:MyScript \ --sender --slow --broadcast --unlocked --rpc-url {} ``` * `sender_address` can be found in your Fireblocks workspace, through Fireblocks API, or using the JSON RPC directly. Get address of vault account 0: ```bash theme={"system"} FIREBLOCKS_API_KEY=12345678-1234-1234-1234-123456789abc \ FIREBLOCKS_API_PRIVATE_KEY_PATH=/path/to/secret.key \ FIREBLOCKS_CHAIN_ID=11155111 \ fireblocks-json-rpc --http --vaultAccountIds 0 -- curl {} \ -X POST \ -H "Content-Type: application/json" \ --data '{"method":"eth_accounts","id":1,"jsonrpc":"2.0"}' ``` This should take a couple of minutes to run if everything was configured correctly. # EVM Web3 Provider Source: https://developers.fireblocks.com/reference/evm-web3-provider Popular blockchain choices for building Web3 capabilities are based typically on the Ethereum Virtual Machine (EVM), and therefore share the development tech stack. Some examples of EVM blockchains: * Ethereum * BNB Chain (BSC) * Polygon * Avalanche * Arbitrum # Convenience libraries Most commonly, developers interact with EVM-based blockchains using "convenience libraries" such as [web3.js](https://web3js.readthedocs.io/) and [ethers.js](https://docs.ethers.io). To streamline your JavaScript development experience, we created the [Fireblocks Web3 Provider](https://github.com/fireblocks/fireblocks-web3-provider), to easily connect `ethers.js` and `web3.js` to your Fireblocks workspace. # web3.js integration The [Fireblocks Web3 Provider](https://github.com/fireblocks/fireblocks-web3-provider) helps seamlessly integrate Fireblocks into your [`web3.js`](https://web3js.readthedocs.io/) development stack. Use it to deploy contracts, sign messages, and send transactions. ### Installation ```bash theme={"system"} npm install @fireblocks/fireblocks-web3-provider web3 ``` ### Setup ```javascript theme={"system"} import { FireblocksWeb3Provider, ChainId, ApiBaseUrl } from "@fireblocks/fireblocks-web3-provider"; const eip1193Provider = new FireblocksWeb3Provider({ apiBaseUrl: ApiBaseUrl.Sandbox, // If using a sandbox workspace privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA, }) ``` If you are not using a Sandbox environment, but rather a regular one, just comment the apiBaseUrl line. ### Usage ```javascript theme={"system"} import Web3 from "web3"; const web3 = new Web3(eip1193Provider); ``` Now you can use the `web3` object exactly as you normally would! ### Example In this example we are executing the 'approve' method of [USDC](https://sepolia.etherscan.io/address/0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8#writeContract) (ERC20) token on Sepolia by using web3.js and Fireblocks web3 provider. ```javascript theme={"system"} const { FireblocksWeb3Provider, ChainId, ApiBaseUrl } = require("@fireblocks/fireblocks-web3-provider") const Web3 = require("web3"); // Import the Sepolia USDC ABI const ABI = require("./USDC_SEPOLIA_ABI.json"); // Sepolia USDC Contract Address const CONTRACT_ADDRESS = "0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8" const eip1193Provider = new FireblocksWeb3Provider({ privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA, // apiBaseUrl: ApiBaseUrl.Sandbox // If using a sandbox workspace }); (async() => { const web3 = new Web3(eip1193Provider); const myAddr = await web3.eth.getAccounts() const contract = new web3.eth.Contract(ABI, CONTRACT_ADDRESS); const spenderAddr = "" // 1 USDC to approve const amount = 1e6 // Invoke approve method console.log( await contract.methods.approve(spenderAddr, amount).send({ from: myAddr[0] }) ) })().catch(error => { console.log(error) }); ``` # ethers.js integration The [Fireblocks Web3 Provider](https://github.com/fireblocks/fireblocks-web3-provider) helps seamlessly integrate Fireblocks into your [`ethers.js`](https://docs.ethers.io/) development stack. You can use it to deploy contracts, sign messages, and send transactions. ### Installation ```bash theme={"system"} npm install @fireblocks/fireblocks-web3-provider ethers@5 ``` ### Setup ```javascript theme={"system"} import { FireblocksWeb3Provider, ChainId, ApiBaseUrl } from "@fireblocks/fireblocks-web3-provider"; const eip1193Provider = new FireblocksWeb3Provider({ apiBaseUrl: ApiBaseUrl.Sandbox, // If using a sandbox workspace privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA, }) ``` If you are not using a Sandbox environment, but rather a regular one, just comment the apiBaseUrl line. ### Usage ```javascript theme={"system"} import * as ethers from "ethers" const provider = new ethers.providers.Web3Provider(eip1193Provider); ``` Now you can use the `provider` object exactly as you normally would! ### Example In this example we are executing the 'approve' method of [USDC](https://sepolia.etherscan.io/address/0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8#writeContract) (ERC20) token on Sepolia by using ethers.js and Fireblocks web3 provider. ```javascript theme={"system"} const { FireblocksWeb3Provider, ChainId, ApiBaseUrl } = require("@fireblocks/fireblocks-web3-provider") const ethers = require("ethers") // Import the Sepolia USDC ABI const ABI = require("./USDC_SEPOLIA_ABI.json"); // Sepolia USDC Contract Address const CONTRACT_ADDRESS = "0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8" const eip1193Provider = new FireblocksWeb3Provider({ privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA, // apiBaseUrl: ApiBaseUrl.Sandbox // If using a sandbox workspace }); (async() => { const provider = new ethers.providers.Web3Provider(eip1193Provider); const myContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider.getSigner()); const spenderAddr = "" // 1 USDC to approve const amount = 1e6 // Invoke approve method const tx = await myContract.approve( spenderAddr, amount ) console.log(JSON.stringify(tx, null, 2)) })().catch(error => { console.log(error) }); ``` # Viem integration The [Fireblocks Web3 Provider](https://github.com/fireblocks/fireblocks-web3-provider) helps seamlessly integrate Fireblocks into your [viem](https://viem.sh/) development stack. You can use it to deploy contracts, sign messages, and send transactions. ### Installation ```bash theme={"system"} npm install @fireblocks/fireblocks-web3-provider viem ``` ### Setup ```javascript theme={"system"} import { FireblocksWeb3Provider, ChainId, ApiBaseUrl } from "@fireblocks/fireblocks-web3-provider"; const eip1193Provider = new FireblocksWeb3Provider({ apiBaseUrl: ApiBaseUrl.Sandbox, // If using a sandbox workspace privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA, }) ``` If you are not using a Sandbox environment, but rather a regular one, just comment the apiBaseUrl line. ### Usage ```javascript theme={"system"} const { createWalletClient, custom } = require("viem") const { sepolia } = require("viem/chains") const walletClient = createWalletClient({ chain: sepolia, transport: custom(eip1193Provider), }); ``` Now you can use the `walletClient` object exactly as you normally would! ### Example In this example we are executing the 'approve' method of [USDC](https://sepolia.etherscan.io/address/0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8#writeContract) (ERC20) token on Sepolia by using *viem* and Fireblocks web3 provider. ```javascript theme={"system"} const { sepolia } = require("viem/chains") const { ChainId, FireblocksWeb3Provider, ApiBaseUrl } = require("@fireblocks/fireblocks-web3-provider") const { createWalletClient, custom, createPublicClient, http } = require("viem") // Import the Sepolia USDC ABI const ABI = require("./USDC_SEPOLIA_ABI.json"); // Sepolia USDC Contract Address const CONTRACT_ADDRESS = '0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8' (async () => { const spenderAddr = ""; // 1 USDC to approve const amount = 1e6; const eip1193Provider = new FireblocksWeb3Provider({ // apiBaseUrl: ApiBaseUrl.Sandbox, // If using a sandbox workspace privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA }); // Create wallet client instance const walletClient = createWalletClient({ chain: sepolia, transport: custom(eip1193Provider), }); // Create public client instance const publicClient = createPublicClient({ chain: sepolia, transport: http() }) // Get my account's address const [ account ] = await walletClient.getAddresses(); // Simulate the 'approve' call const { request } = await publicClient.simulateContract({ address: CONTRACT_ADDRESS, abi: ABI, functionName: 'approve', args: [spenderAddr, amount], account }) // Execute approve call via Fireblocks await walletClient.writeContract(request) })(); ``` # Ethereum Smart Contract Development Smart contract development frameworks make it easy to develop, test, and deploy smart contracts on EVM-based blockchains. # Using Truffle Use the [Fireblocks Web3 Provider](https://github.com/fireblocks/fireblocks-web3-provider) to seamlessly work with Truffle on top of Fireblocks. ### Installation ```bash theme={"system"} npm install -g truffle npm install @fireblocks/fireblocks-web3-provider ``` ## Configuration ### Step 1: Set the [environment variables](https://github.com/fireblocks/fireblocks-json-rpc/blob/main/.env.example) ### Step 2: Initialize an example Truffle NFT project: ```bash theme={"system"} truffle unbox nft-box ``` ### Step 3: Edit the **truffle-config.js** file: ```javascript theme={"system"} require('dotenv').config(); const { FireblocksWeb3Provider, ChainId } = require("@fireblocks/fireblocks-web3-provider"); module.exports = { networks: { sepolia: { provider: () => new FireblocksWeb3Provider({ privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, chainId: ChainId.SEPOLIA, }), network_id: ChainId.SEPOLIA, }, }, compilers: { solc: { version: "0.8.13" } }, }; ``` ### Usage: ```bash theme={"system"} truffle migrate --network sepolia ``` # EW API errors Source: https://developers.fireblocks.com/reference/ew-api-errors This page covers API errors associated with the Fireblocks Embedded Wallet. Every error is accompanied by the correct HTTP error code and a message structured as follows: ```javascript theme={"system"} { error: 'HTTP Error description', message: 'Error message' } ``` # 4XX errors ## 400 - Bad Request | Error Name | Error Message | | :- | :- | | No active webhook subscriptions | The workspace was not configured with a Webhook endpoint that is required for NCW async communication. | | Unsupported algorithm | The provided algorithm for MPC key generation is not supported. Learn more [here](https://ncw-developers.fireblocks.com/docs/mpc-key-generation). | | Pagination limit too large | The provided pagination limit parameter is higher than 50. | | Validation failed (numeric string is expected) | The API call was made with an invalid NCW wallet identifier or account ID. | ## 403 - Forbidden | Error Name | Error Message | | :- | :- | | Wallet disabled | The requested NCW is disabled. Learn more [here](/api-reference/embedded-wallets/update-wallet-status). | | Device disabled | The requested device is disabled. Learn more [here](/api-reference/embedded-wallets/update-device-status). | | Tenant disabled | The entire Fireblocks workspace is disabled. | ## 404 - Not Found | Error Name | Error Message | | :- | :- | | Wallet not found | The provided wallet ID was not found. | | Device not found | The provided device ID was not found. | | Tenant not found | The provided tenant ID was not found. | ## 409 - Conflict | Error Name | Error Message | | :- | :- | | Incomplete MPC Setup | The MPC Key generation was not completed. | | Ongoing setup already in motion | MPC Key generation ceremony is in process. | *** # 1XXX errors ## 1000 - Incomplete Wallet | Error Name | Error Message | | :- | :- | | Incomplete Wallet | None of the devices in the Wallet have a COMPLETE status. Learn more [here](https://ncw-developers.fireblocks.com/docs/multiple-algorithms). | ## 1001 - Incomplete Device | Error Name | Error Message | | :- | :- | | Incomplete Device | The device key status is in status INCOMPLETE. Learn more [here](https://ncw-developers.fireblocks.com/docs/multiple-algorithms). | ## 1002 - Incomplete Backup | Error Name | Error Message | | :- | :- | | Incomplete Backup | The device key was not backed up. Learn more about backups [here](https://ncw-developers.fireblocks.com/docs/backup-recovery-1) and multiple algorithms [here](https://ncw-developers.fireblocks.com/docs/multiple-algorithms). | ## 1003 - Unsupported Algorithms | Error Name | Error Message | | :- | :- | | Unsupported Algorithms | The Algorithm is not supported by the workspace. Learn more [here](https://ncw-developers.fireblocks.com/docs/multiple-algorithms). | # Exchange Account Objects Source: https://developers.fireblocks.com/reference/exchange-objects ## ExchangeAccount | Parameter | Type | Description | | - | - | - | | id | string | The ID of the exchange asset to return | | type | [ExchangeType](#exchangetype) | The name of the exchange the account is associated with | | name | string | Display name of the exchange account | | status | [ConfigChangeRequestStatus](/reference/general-objects#configchangerequeststatus) | Status of the exchange account connection | | assets | Array of [ExchangeAsset](#exchangeasset) | Assets in the account | | isSubaccount | boolean | True if the account is a subaccount in an exchange | | mainAccountId | string | The ID of the exchange main account | | tradingAccounts | Array of [TradingAccount](#tradingaccount) | Trading accounts under this exchange account | | fundableAccountType | [TradingAccountType](#tradingaccounttype) | The internal account that is used for deposits or withdrawals of this exchange's main or subaccount | *** ## ExchangeAsset | Parameter | Type | Description | | - | - | - | | id | string | The ID of the exchange asset to return | | total | string | The total balance of the asset in the exchange account | | available | string | The balance that can be withdrawn from the exchange account or moved to a different account | | lockedAmount | string | Locked amount in the account | | balance | string | Deprecated - replaced by "total" | *** ## ExchangeType | Parameter | Type | Description | | - | - | - | | type | string | \[BINANCE, BINANCEUS, BITFINEX, BITHUMB, BITMEX, BITSO, BITSTAMP, BITTREX, BYBIT, CIRCLE, COINBASEEXCHANGE, COINBASEPRO, COINMETRO, COINSPRO, CRYPTOCOM, DERIBIT, GATE, GEMINI, HITBTC, HUOBI, IR, KORBIT, KRAKEN, KRAKENINTL, LIQUID, POLONIEX, OKCOIN, OKEX, SEEDCX] | > [!NOTE] > > * The `BINANCEUS` value should be used only by Binance.us customers. > * The `KRAKENINTL` value should be used only by non-US Kraken customers. *** ## TradingAccount | Parameter | Type | Description | | - | - | - | | type | [TradingAccountType](#tradingaccounttype) | The specific trading account under the exchange account | | assets | Array of [ExchangeAsset](#exchangeasset) | Assets in the trading account | *** ## TradingAccountType | Parameter | Type | Description | | - | - | - | | type | string | \[SPOT, FUTURES, MARGIN, FUNDING, OPTIONS, EXCHANGE, COIN\_MARGINED\_SWAP, USDT\_FUTURES, COIN\_FUTURES, USDT\_ISOLATED\_MARGINED\_SWAP, USDT\_MARGINED\_SWAP\_CROSS, FUNDABLE, UNIFIED] | # Execute Payouts Source: https://developers.fireblocks.com/reference/executing-payouts # Creating a payout instruction set When a user wants to send a payment to one or more of their merchants, they can use the Payout Service API to send the payout from their exchange account. First, the user creates a payout instruction set using the [Create a payout instruction set](/reference/createpayout) API call. The instruction set defines: * the payment account and its account type (vault, exchange, or fiat). * the account type (vault account, exchange account, whitelisted address, network connection, fiat account, or merchant account), the amount, and the asset of payment for each payee account. In this example, the user wants to send funds from an exchange account (id: “EX\_SUB3”) in their Fireblocks workspace. They want to send 43 USDC to Merchant 1 (id: “bef85a1c-b605-4b2e-bdb5-2d400f4d0bf3”) who is configured as an external wallet and 4,423 USDC to Merchant 2 (id: “3adc1f92-e791-44a8-9aee-7f31c2108b78”) who is configured as a Fireblocks Network connection. ## Example ```json theme={"system"} { "paymentAccount": { "id": "EX_SUB3", "type": "EXCHANGE_ACCOUNT" }, "instructionSet": [ { "payeeAccount": { "id": "bef85a1c-b605-4b2e-bdb5-2d400f4d0bf3", "type": "EXTERNAL_WALLET" }, "amount": { "amount": "43", "assetId": "USDC" } }, { "payeeAccount": { "id": "3adc1f92-e791-44a8-9aee-7f31c2108b78", "type": "NETWORK_CONNECTION" }, "amount": { "amount": "4423", "assetId": "USDC" } } ] } ``` ## Response The user then receives a unique payoutId ("1fe3b61f-7e1f-4a19-aff0-4f0a524d44d7") for their payout instruction set. This payoutId can then be used for executing the payout. The response body also lists the state of the payout instruction set as REQUESTED and the two payment states as NOT\_STARTED. Following are descriptions of the parameters in the response: * **payoutId**: unique ID for the payout * **paymentAccount** * **id**: the ID of the payment account * **type**: the type of payment account (vault account, exchange account, or fiat account) * **createdAt**: when the instruction set was created * **state**: the state of the instruction set * **initMethod**: method of creating the instruction set * **instructionSet**: an array of instructions for the payout. Each one details the payee, the payment amount, and the status of the payment. * **id**: the ID for the individual payment in the instruction set * **name**: the name of the payee * **payeeAccount** * **id**: the ID of the account receiving the payment * **type**: the type of account receiving the payment * **amount** * **amount**: the amount being sent * **assetId**: the asset being sent * **state**: the status of the individual payment * **transactions**: the transactions included in the payment ```json theme={"system"} { "payoutId": "1fe3b61f-7e1f-4a19-aff0-4f0a524d44d7", "paymentAccount": { "id": "EX_SUB3", "type": "EXCHANGE_ACCOUNT" }, "createdAt": 1645365800, "state": "REQUESTED", "initMethod": "API", "instructionSet": [ { "id": "6ea4a016-536b-49af-b1a0-40b343ccf879", "name": "Merchant 1", "payeeAccount": { "id": "bef85a1c-b605-4b2e-bdb5-2d400f4d0bf3", "type": "EXTERNAL_WALLET" }, "amount": { "amount": "43", "assetId": "USDC" }, "state": "NOT_STARTED", "transactions": [] }, { "id": "e783a79b-6acc-4d18-885d-ed533cad8eeb", "name": "Merchant 2", "payeeAccount": { "id": "3adc1f92-e791-44a8-9aee-7f31c2108b78", "type": "NETWORK_CONNECTION" }, "amount": { "amount": "4423", "assetId": "USDC" }, "state": "NOT_STARTED" "transactions": [] } ] } ``` *** # Executing a payout instruction set Once the payout instruction set is created, the user can execute the payout using the [Execute a payout instruction set](/reference/executepayoutaction) API call and entering the payoutId of the instruction set that was just created ("1fe3b61f-7e1f-4a19-aff0-4f0a524d44d7"). *** # Monitoring payouts The user can also monitor the status of the newly executed payout using the [Get the status of a payout instruction set](/reference/getpayout) API call and entering the payoutId. ## Example `GET /payments/payout/1fe3b61f-7e1f-4a19-aff0-4f0a524d44d7` ## Response The state and the payout status are returned. ```json theme={"system"} { "payoutId": "1fe3b61f-7e1f-4a19-aff0-4f0a524d44d7", "paymentAccount": { "id": "EX_SUB3", "type": "EXCHANGE_ACCOUNT" }, "createdAt": 1645365800, "state": "FINALIZED", "status": " DONE" "initMethod": "API", "instructionSet": [ { "id": "6ea4a016-536b-49af-b1a0-40b343ccf879", "name": "Merchant 1", "payeeAccount": { "id": "bef85a1c-b605-4b2e-bdb5-2d400f4d0bf3", "type": "EXTERNAL_WALLET" }, "amount": { "amount": "4312", "assetId": "USDC" }, "state": "COMPLETED", "transactions": [{ "id": "35a4b10c-1f83-4f0b-ba2a-da0e73be2d6e", "state": "COMPLETED", "timestamp": 1645367429 }] }, { "id": "e783a79b-6acc-4d18-885d-ed533cad8eeb", "name": "Merchant 2", "payeeAccount": { "id": "3adc1f92-e791-44a8-9aee-7f31c2108b78", "type": "NETWORK_CONNECTION" }, "amount": { "amount": "4423", "assetId": "USDC" }, "state": "COMPLETED", "transactions": [{ "id": "4505e7d9-bfc7-41bc-9750-54311fcbbf26", "state": "COMPLETED", "timestamp": 1645367449 }] } } ], "reportUrl": "https://some-url.com/reports/cc5777c1-75a9-4337-aebd-f1f5a40a9391" } ``` # Postman Guide Source: https://developers.fireblocks.com/reference/explore-postman-collection Postman is an application designed to help with API integration and exploration. Intuitive for different tech skill levels, this is the tool of choice both for experienced developers and no-code enthusiasts to get familiar with our available endpoints, requests, and responses. Using our Postman Collection, you can start testing our API before you write a single line of code. ## Install Postman and Fireblocks Collection 1. Download and install the [Postman app](https://www.postman.com/downloads/) or [use Postman online](https://identity.getpostman.com/login?continue=https%3A%2F%2Fweb.postman.co%2Fhome). 2. **Import the Fireblocks collection:** [![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/25881726-6597f15a-56e5-4c82-a75e-2befc2c5264d?action=collection%2Ffork\&collection-url=entityId%3D25881726-6597f15a-56e5-4c82-a75e-2befc2c5264d%26entityType%3Dcollection%26workspaceId%3Dddfe6a6b-2c7b-4005-89f3-7204e03ec3b6%3Fenv%3DBoilerplate%2520Fireblocks%2520Environment%253D%253D) After following the steps above and opening Postman, you'll see the **Fireblocks API** collection. ## Set up authentication The Fireblocks Postman collection signs each request with a JSON Web Token (JWT) using your API credentials. The collection includes a pre-request script that reads your API key and secret key from a **Postman environment** you create. The collection does not include credentials or an environment file. > [!NOTE] > > **New to API authentication?** Follow [Step 1 in the Quickstart](/docs/quickstart#step-1-set-up-api-authentication) to generate a CSR, create an API user, and obtain your credentials. For ongoing key management, see [Manage API Access](/docs/manage-api-keys). ### Create a Postman environment > [!WARNING] > > **Mind your environment's scope.** Postman environments can live in a personal workspace (private to you) or a shared/team workspace, where they sync to every member with access. Create your Fireblocks environment in your **personal workspace**, not a shared or team workspace, and keep `fireblocksSecretKey` there only. Treat it with the same care as your private key file — anyone who can access the environment can read it. 1. In Postman, select **Environments** in the left sidebar. 2. Select **+** to create a new environment. 3. Name it (for example, `Fireblocks API`). 4. Add the following variables and set their **Current value**: | Variable | Description | | - | - | | `fireblocksApiKey` | Your API key (API User ID) from the Fireblocks Console. Copy it from **Developer Center** > **API Users** after your API user is approved. | | `fireblocksSecretKey` | The full contents of your `fireblocks_secret.key` file, including the `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` lines. Open the file in a text editor and paste the entire contents into the **Current value** field. | 5. Select **Save**. ### Select the environment 1. In the top-right corner of Postman, open the environment dropdown. 2. Select the environment you created. The collection pre-request script requires an active environment. If none is selected, requests fail with an error like `No environment containing a fireblocksApiKey variable has been selected`. ### Set the base URL The collection includes a `baseUrl` collection variable, defaulting to `https://api.fireblocks.io/v1`. Update it to match your workspace: 1. Select the **Fireblocks API** collection in the left sidebar. 2. Open the **Variables** tab. 3. Set `baseUrl` to the value for your workspace: | Workspace | `baseUrl` value | | - | - | | US Sandbox | `https://sandbox-api.fireblocks.io/v1` | | US Mainnet or Testnet | `https://api.fireblocks.io/v1` | | EU Mainnet or Testnet | `https://eu-api.fireblocks.io/v1` | | EU2 Mainnet or Testnet | `https://eu2-api.fireblocks.io/v1` | 4. Select **Save**. > [!TIP] > > **Sandbox users:** Developer Sandboxes include a pre-created API user. You can download the API key and secret key from the Sandbox Console instead of generating your own CSR. See the [Quickstart](/docs/quickstart#step-1-set-up-api-authentication) for details. ### Verify your setup 1. Confirm your Fireblocks environment is selected in the top-right environment dropdown. 2. Open any GET request in the **Fireblocks API** collection (for example, a vault accounts endpoint). 3. Select **Send**. A `200` response confirms authentication is working. If you receive an authentication error, check the troubleshooting tips below. ### Troubleshooting > [!WARNING] > > Store your API key and secret key only in your Postman environment. Do not share screenshots that include these values. * **No environment selected** — Select the environment you created before sending requests. The pre-request script reads `fireblocksApiKey` and `fireblocksSecretKey` from the active environment only. * **Empty `fireblocksSecretKey`** — Paste the full PEM contents of your private key file, including the header and footer lines. The pre-request script rejects values that do not start with `-----BEGIN PRIVATE KEY-----`. * **Duplicate variables** — Keep `fireblocksApiKey` and `fireblocksSecretKey` in your environment only. If the same variables also exist as collection variables, remove or clear the collection-level copies. * **Wrong `baseUrl`** — Include the `/v1` suffix (for example, `https://sandbox-api.fireblocks.io/v1`, not `https://sandbox-api.fireblocks.io`). Set `baseUrl` on the collection **Variables** tab. * **404 errors** — A missing or incorrect `baseUrl` often causes 404 responses. Double-check the value against your workspace type in [Workspace Comparison](/docs/workspace-environments). For details on how request signing works, see [Authenticate](/reference/signing-a-request-jwt-structure). # Fee Estimation Objects Source: https://developers.fireblocks.com/reference/fee-estimation-objects ## NetworkFee | Parameter | Type | Description | | - | - | - | | feePerByte | string | \[optional] For UTXOs | | gasPrice | string | \[optional] For Ethereum assets (ETH and Tokens) | | networkFee | string | \[optional] All other assets | | baseFee | string | \[optional] Base Fee according to EIP-1559 (ETH assets) | | priorityFee | string | \[optional] Priority Fee according to EIP-1559 (ETH assets) | *** ## TransactionFee | Parameter | Type | Description | | - | - | - | | feePerByte | string | \[optional] For UTXOs, | | gasPrice | string | \[optional] For Ethereum assets (ETH and Tokens) | | gasLimit | string | \[optional] For Ethereum assets (ETH and Tokens), the limit for how much can be used | | networkFee | string | \[optional] Transaction fee | | baseFee | string | \[optional] Base Fee according to EIP-1559 (ETH assets) | | priorityFee | string | \[optional] Priority Fee according to EIP-1559 (ETH assets) | *** ## EstimatedTransactionFeeResponse | Parameter | Type | Description | | - | - | - | | low | [TransactionFee](#transactionfee) | Transactions with this fee will probably take longer to be mined | | medium | [TransactionFee](#transactionfee) | Average transactions fee | | high | [TransactionFee](#transactionfee) | Transactions with this fee should be mined the fastest | ## EstimatedFeeDetails | Parameter | Type | Description | | - | - | - | | low | [FeeBreakdown - Solana Specific](#feebreakdown-solana-specific) OR [FeeBreakdown - Generic](#feebreakdown-generic) | Detailed fee breakdown where the transaction will probably take longer to be mined | | medium | [FeeBreakdown - Solana Specific](#feebreakdown-solana-specific) OR [FeeBreakdown - Generic](#feebreakdown-generic) | Average transactions fee | | high | [FeeBreakdown - Solana Specific](#feebreakdown-solana-specific) OR [FeeBreakdown - Generic](#feebreakdown-generic) | Detailed fee breakdown where the transaction will be mined the fastest | ## FeeBreakdown - Solana Specific | Parameter | Type | Description | | - | - | - | | baseFee | string | Base fee for Solana transaction | | priorityFee | string | Priority fee for Solana transaction | | rent | string | Rent fee for Solana account creation/storage | | totalFee | string | Total fee amount | ## FeeBreakdown - Generic | Parameter | Type | Description | | - | - | - | | baseFee | string | Base fee component | | priorityFee | string | Priority fee component | | totalFee | string | Total fee amount | # Fiat Account Objects Source: https://developers.fireblocks.com/reference/fiat-account-objects ## FiatAccount | Parameter | Type | Description | | - | - | - | | id | string | The ID of the account | | type | string | \[ BLINC ] | | name | string | The display name of the fiat account | | address | string | The address of the account | | assets | Array of [FiatAssets](#fiatasset) | An array of the assets under the account | *** ## FiatAsset | Parameter | Type | Description | | - | - | - | | id | string | The ID of the asset | | balance | string | The balance of the asset | # Fund the Gas Station Source: https://developers.fireblocks.com/reference/fund-the-gas-station Maintaining funds in the Gas Station tank is critical to avoid business interruptions. Transactions made from vault accounts with insufficient gas will continuously fail until you add more funds. The Gas Station tank is the vault account or Wallet Pool you designated in **Settings** > **Gas Station tank**. Fund it by transferring the base asset of each network you want to fuel (for example, ETH for Ethereum) into the tank. The Gas Station then uses these assets to fuel your auto-fueled vault accounts with gas for their outbound transactions. If the tank is a Wallet Pool, fund its member vault accounts. See [Wallet Pools](/docs/wallet-pools#use-cases). *** # Funding with the API Call the [Create a new transaction endpoint](/api-reference/transactions/create-a-new-transaction), enter **VAULT\_ACCOUNT** in the destination object's **type** field and the tank's vault account ID in the **id** field, and then enter the appropriate base asset (as [defined in Fireblocks](/api-reference/blockchains-&-assets/list-assets-legacy)) in the **assetId** field. The example below transfers 0.5 ETH from vault account ID 1 to the Gas Station tank in vault account ID 2. ```typescript TypeScript theme={"system"} const fundGasStationTank = async ( payload: TransactionRequest, ): Promise => { try { const result = await fireblocks.transactions.createTransaction({ transactionRequest: payload, }); console.log(JSON.stringify(result.data, null, 2)); return result.data; } catch (e) { console.error(e); } }; fundGasStationTank({ assetId: "ETH", // update the assetId source: { type: TransferPeerPathType.VaultAccount, id: "1", // update to your funding vault account ID }, destination: { type: TransferPeerPathType.VaultAccount, id: "2", // update to your Gas Station tank vault account ID }, amount: "0.5", note: "Gas Station tank funding", }); ``` ```python Python theme={"system"} from fireblocks.models.transaction_request import TransactionRequest def fund_gas_station_tank(asset_id: str, amount: str, funding_vault_id: str, tank_vault_id: str): transaction_request = TransactionRequest.from_dict({ "assetId": asset_id, "source": {"type": "VAULT_ACCOUNT", "id": funding_vault_id}, "destination": {"type": "VAULT_ACCOUNT", "id": tank_vault_id}, "amount": amount, "note": "Gas Station tank funding", }) result = fireblocks.transactions.create_transaction(transaction_request=transaction_request).result() print(result.data) fund_gas_station_tank("ETH", "0.5", "1", "2") ``` # Gas Station Objects Source: https://developers.fireblocks.com/reference/gas-station-objects ## GasStationPropertiesResponse | Parameter | Type | Description | | - | - | - | | balance | dictionary | A dictionary of an asset and its balance in the Gas Station tank | | configuration | [GasStationConfiguration](#gasstationconfiguration) | The settings of your Gas Station | *** ## GasStationConfiguration | Parameter | Type | Description | | - | - | - | | gasThreshold | string | Below this ETH balance the address will be funded up until the gasCap value, in ETH units | | gasCap | string | Up to this level, the address will be funded with ETH, in ETH units | | maxGasPrice | string | The funding transaction will be sent with this maximum value gas price, in Gwei units | # General Objects Source: https://developers.fireblocks.com/reference/general-objects ## ConfigChangeRequestStatus | Parameter | Type | Description | | - | - | - | | status | string | \[ WAITING\_FOR\_APPROVAL, APPROVED, CANCELLED, REJECTED, FAILED ] | *** ## Paging | Parameter | Type | Description | | - | - | - | | next | string | Cursor to the next page. | # Handle API Errors Source: https://developers.fireblocks.com/reference/handling-api-errors Understanding errors and how to handle them is critical to user experience and business operations when working with *any* third-party API. Some errors might include: * A timeout as the third-party service is experiencing issues or is down. * An improperly formatted request due to user error or a non-fatal software bug. * A runtime error due to a system state or an "unexpected" error. These types of errors are important to handle when working with third-party APIs and handling individual errors will depend on the nature of each API call. ## Error types In this section, we dive into how to handle API errors when using the Fireblocks API, in terms of best practices and common pitfalls. As the Fireblocks API uses HTTP requests to send the calls, we will look into three main error types: 1. [Non-HTTP errors](#non-http-errors) 2. [4xx status codes](#4xx-status-codes) 3. [500 status code](#500-status-code) > [!NOTE] > > **How to handle unspecified errors:** While we do our best to cover all the errors that are possible, and are constantly improving error reporting, you might encounter an error you did not read about in this guide, or the approach and best practices do not suffice. > > We recommend reading the message that accompanies every Fireblocks API error, as these are usually descriptive and can help pinpoint the issue. *** ## Non-HTTP errors Non-HTTP errors are a broad error type that relates to anything that is not specifically a response back from the Fireblocks API. As a result, this error type may contain many individual errors that can typically be resolved with the relevant third-party documentation. Examples of such errors include: * Errors that prevent the execution of `.js` or `.py` (or any other extension) files such as `command not found`, or `No such file or directory` * Errors relating to internal formatting of a file (missing indent, missing bracket, `==` instead of `===`) * Errors relating to system state, such as lack of memory, or network connectivity issues As described in our API guides, signing a JWT (JSON Web Token) is a critical part of API usage as the means of authenticating your message and validating your identity. (This assumes the private key used to sign your API request is securely stored and not available to anyone else). You may be unable to sign the JWT token if you are experiencing issues with your private key. These issues are classified as "**private key corruption**". While uncommon, it can be a serious issue when trying to sign API requests. ### Private key corruption Observe the following error message: (If you are unfamiliar with this error, a Google search will yield many results pointing to authentication problems.) ```text theme={"system"} Error: Error: error:1E08010C:DECODER routines::unsupported at Sign.sign (/myproject/lib/internal/crypto/sig.js:131:29) at Object.sign /myproject/node_modules/jwa/index.js:152:45) at Object.jwsSign [as sign] (/myproject/node_modules/jws/lib/sign-stream.js:32:24) at module.exports [as sign] (/myproject/node_modules/jsonwebtoken/sign.js:204:16) at ApiTokenProvider.signJwt (/myproject/fireblocks-sdk-js/src/api-token-provider.ts:11:28) at ApiClient. (/myproject/fireblocks-sdk-js/src/api-client.ts:15:41) at Generator.next () at /myproject/fireblocks-sdk-js/dist/api-client.js:8:71 at new Promise () at __awaiter (/myproject/fireblocks-sdk-js/dist/api-client.js:4:12) at ApiClient.issueGetRequest (/myproject/fireblocks-sdk-js/dist/api-client.js:27:16) at FireblocksSDK. (/myproject/fireblocks-sdk-js/src/fireblocks-sdk.ts:537:37) at Generator.next () at /myproject/fireblocks-sdk-js/dist/fireblocks-sdk.js:18:71 at new Promise () at __awaiter (/myproject/fireblocks-sdk-js/dist/fireblocks-sdk.js:14:12) ``` ```text theme={"system"} Traceback (most recent call last): File "/myproject/venv/lib/python3.10/site-packages/jwt/algorithms.py", line 257, in prepare_key key = load_pem_private_key(key, password=None) File "/myproject/venv/lib/python3.10/site-packages/cryptography/hazmat/primitives/serialization/base.py", line 22, in load_pem_private_key return ossl.load_pem_private_key(data, password) File "/myproject/venv/lib/python3.10/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 900, in load_pem_private_key return self._load_key( File "/myproject/venv/lib/python3.10/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 1168, in _load_key self._handle_key_loading_error() File "/myproject/venv/lib/python3.10/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 1227, in _handle_key_loading_error raise ValueError( ValueError: ('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [_OpenSSLErrorWithText(code=503841036, lib=60, reason=524556, reason_text=b'error:1E08010C:DECODER routines::unsupported')]) ``` The issue within this error is a "corruption" of the private key. "Corruption" can also mean human error such as submitting an incorrect file that is not a private key. Follow the instructions below to resolve this error. 1. Verify that the file being used is indeed a private key. A private key typically looks like this: ```yaml theme={"system"} -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- ``` 2. Verify that the private key is intact. 3. Generate something out of the private key using OpenSSL. This command will attempt to convert the private key into its corresponding public key: `openssl rsa -in .key -pubout` A valid response will look something like this: ```yaml theme={"system"} -----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY----- ``` *** ## 4xx status codes 4xx status codes are returned as part of an HTTP request to indicate a problem on the client's side. In the context of this article, it means there is an issue with the request you sent. We will look into four specific status codes and how to handle each of them: 1. [400 - Bad request](#400-bad-request) - Indicates that the API request itself is incorrect and contains invalid or incorrect values. 2. [401 - Unauthorized](#401-unauthorized) - Indicates that the API request is sent with invalid authentication information (for example, a bad JWT). 3. [403 - Forbidden](#403-forbidden) - Indicates that the API request is trying to perform something the user is not allowed to do. 4. [404 - Not found](#404-not-found) - Indicates that the API request is trying to query a page that does not exist. In addition to these codes, remember that the status code `429 Too many requests` is caused by breaking the rate limits. For more information, see the [Working with rate limits](/reference/rate-limiting) article. The example code below illustrates how to approach each scenario. It might contain functions or types that are not explicitly written out; where that happens, we describe what they do after the code sample. No such type or function exists in the SDK — they merely illustrate a logical container for an operation. Throughout each error handling section, the examples assume that: * The user input ***may not*** be valid, whether through a function call or direct integration. * The network connection is functioning as expected. * The system has sufficient resources (memory and disk space). These assumptions matter for security and stability, as they show how to ensure your information is valid before submitting the request and how to double-check or sanitize the user input. [Typical validations](#common-validations-to-perform) are provided at the bottom of the article. ### 400 - Bad request As mentioned above, 400 response codes indicate that the request you sent contains incorrect information or is invalid. #### 400 example - Bad request Your internal database links users per asset public keys with their internal database reference (for example, their user ID). To do this, upon registration or upon some update, your code calls the following `getPublicKey`\ `get_public_key` function with the asset **supplied by the user**: ```javascript JavaScript theme={"system"} const DEFAULT_VAULT_ACCOUNT_ID = "123"; /** fbksSdk - an instance of FireblocksSDK asset - the asset id */ async function getPublicKey(fbksSdk, asset){ let pubKey = await fbks.getPublicKeyInfoForVaultAccount({ vaultAccountId: DEFAULT_VAULT_ACCOUNT_ID, assetId:asset, compressed:true, addressIndex:"0", change:"0" }); //... Some extra work on the public key ... } ``` ```python Python theme={"system"} DEFAULT_VAULT_ACCOUNT_ID = "123"; def get_public_key(fbks, asset): """ fbks - FireblocksSDK instance asset - the asset id """ pub_key = fbks.get_public_key_info_for_vault_account( vault_account_id=DEFAULT_VAULT_ACCOUNT_ID, asset_id=asset, compressed=False, change="0", address_index="0" ) # ... Some extra work on the public key ... ``` The user mistakenly put an invalid asset (for example `BTC1` instead of `BTC`). Your code will receive the following error: ```text theme={"system"} Error: Request failed with status code 400 at createError (/myproject/fireblocks-sdk-js/node_modules/axios/lib/core/createError.js:16:15) at settle (/myproject/fireblocks-sdk-js/node_modules/axios/lib/core/settle.js:17:12) at IncomingMessage.handleStreamEnd (/myproject/fireblocks-sdk-js/node_modules/axios/lib/adapters/http.js:293:11) at IncomingMessage.emit (node:events:525:35) at endReadableNT (node:internal/streams/readable:1359:12) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) ``` ```text theme={"system"} Traceback (most recent call last): File "/myproject/main.py", line 10, in get_public_key(fbksSdk, "jngjewqn") File "/myproject/main.py", line 8, in get_public_key fbks.get_public_key_info_for_vault_account(vault_account_id="2", asset_id=asset, compressed=False, change="0", address_index="0") File "/myproject/venv/lib/python3.10/site-packages/fireblocks_sdk/sdk.py", line 1066, in get_public_key_info_for_vault_account return self._get_request(url) File "/myproject/venv/lib/python3.10/site-packages/fireblocks_sdk/sdk.py", line 1334, in _get_request return handle_response(response, page_mode) File "/myproject/venv/lib/python3.10/site-packages/fireblocks_sdk/sdk.py", line 22, in handle_response raise FireblocksApiException("Got an error from fireblocks server: " + response.text, error_code) fireblocks_sdk.api_types.FireblocksApiException: Got an error from fireblocks server: { "message":"The asset 'rando' is not supported by Fireblocks, please check the supported assets endpoint.", "code":1503 } ``` For cases where you receive a 400 HTTP status code error, try using a `try{}catch{}`\ `try:...except...` block. This can be used to handle the error in the proper way, like notifying the user or adjusting the input parameters before attempting the call again. The following is an example of how these types of 400-based errors can be handled for the specific scenario we described: ```javascript JavaScript theme={"system"} const DEFAULT_VAULT_ACCOUNT_ID = "123"; /** fbksSdk - an instance of FireblocksSDK asset - the asset id */ async function getPublicKey(fbksSdk, asset){ let pubKeyResponse = undefined; try{ pubKeyResponse = await fbks.getPublicKeyInfoForVaultAccount({ vaultAccountId: DEFAULT_VAULT_ACCOUNT_ID, assetId:asset, compressed:true, addressIndex:"0", change:"0" }); } catch (e) { let response = e.response; if(response.status < 400 || response.status >= 500){ // Non request based error // We assume that execution of this function will be halted after this block is done } let respData = response.data; if(respData.code === 1503){ // This is discussed later on in the article return new Error("The asset you specified is invalid, please verify that you're sending the correct asset."); } // Other handling } //... Some extra work on the public key ... } ``` ```python Python theme={"system"} DEFAULT_VAULT_ACCOUNT_ID = "123" # fbksSdk - an instance of FireblocksSDK # asset - the asset id def get_public_key(fbks_sdk, asset): pub_key = None try: pub_key = fbks_sdk.get_public_key_info_for_vault_account( vault_account_id=DEFAULT_VAULT_ACCOUNT_ID, asset_id=asset, compressed=True, address_index=0, change=0 ) except FireblocksApiException as e: if e.error_code == 1503: raise Exception("The asset you specified is invalid, please verify that you're sending the correct asset.") # Other handling # ... Some extra work on the public key ... ``` In both code samples, start by verifying that you received a 4xx response code (for 5xx refer to the information below). Then, you'll get the call response data and reference the parameter called `code` which represents the error code returned for the request. Each error code indicates a different issue with the request. [Refer to our API responses page to learn more.](/reference/api-error-codes) The Fireblocks Python SDK does this seamlessly for 4xx and 5xx errors, so handling should only consider the error code or message. #### Handling a 400 error When a 400 response is returned from the Fireblocks API, you will receive the following message: ```json theme={"system"} { error_code: number (optional) message: string } ``` This message will provide a description to inform you of any potential issues. A best practice for error handling that you can see below is setting up a proper error handling flow for sensitive error code responses. This is done by first outlining the following: 1. Identify the features/components you're using - Are you performing wallet-specific operations (whitelisting, creating a new wallet, adding a deposit address to a wallet, etc.)? 2. Identify the user / system-provided inputs - What is constant? What is received as part of the system state (a database query, a read from a file, etc.)? What is received from user input? 3. Identify the potential errors from the [API responses page](/reference/api-error-codes). After you've identified the points above, prepare your error handling for the relevant API calls to best fit your needs (inform the user, run a runtime fix of the system state, etc.). #### Implementing 400 error handling The code sample and general flow below are customizable to fit your code, business logic, or existing implementation. Adapt the code to your preferred language, as well as your business-specific practices, regulations, and systems. Treat the errors the same way: those written in this section are examples and should be changed to work with your flow. The most important takeaway is to identify the components you'll be using and what potential errors might occur based on the input you receive. For example, you're working on a system that receives a request from a user to perform a withdrawal of some amount of a given asset from their Fireblocks asset wallet address. 1. Refresh the balance of the specific asset they want to withdraw from the vault account assigned to this user - using the [refresh asset balance data](/api-reference/vaults/refresh-asset-balance-data) operation. 2. Create a transaction to send the asset from their vault account to the target address - using the [create transaction](/api-reference/transactions/create-a-new-transaction) operation. The refresh balance operation uses a vault account (`vaultAccountId`) and an asset (`assetId`), while the create transaction has many potential parameters. You can narrow down the cause of the error by going through each operation's requirements for your desired end result. 1. The [refresh asset balance data](/api-reference/vaults/refresh-asset-balance-data) operation requires a valid vault account ID and a valid asset. 2. The [create transaction](/api-reference/transactions/create-a-new-transaction) operation requires: 1. A valid asset (can be assumed valid after operation #1 takes place) 2. A valid amount of said asset (which does not exceed what they have in the wallet) 3. A valid target address Referencing the [API responses page](/reference/api-error-codes) shows that, given the operation requirements, you should expect to see these error codes: 1. `1503` - invalid asset 2. `11001` - invalid vault account id You might be asking yourself - what about the amount and the target address? While incorrect in the scope of the example, these values could theoretically be anything (within their given domains, amount as a positive integer, and address as a string of some length). > [!NOTE] > > **Monitoring transaction status:** The failures caused by amount and destination address values are not covered in this guide. For more information about these specific errors, see [Monitoring transaction status](/reference/monitoring-transaction-status). ```javascript JavaScript theme={"system"} // Fireblocks SDK initialized beforehand and is defined as the parameter - fbks // There exists some variable which allows us to query a database for information, defined as - dbSvc async function withdrawal(userId, asset, amount, to){ if(!dbSvc.userExists(userId)){ return new Error(`Unknown user: ${userId}`); } if(!validateToAddress(to, asset)){ return new Error("The address provided does not match conventions for the asset specified."); } // We assume that the information is stored somewhere you are able to retrieve it, but where it's stored is irrelevant, this is merely for the example let userVaultAccountId = dbSvc.getVaultAccountForUser(userId); let assetBalance = undefined; try{ assetBalance = parseFloat((await fbks.refreshVaultAssetBalance(userVaultAccountId, asset)).available); } catch (e) { fbksError(e); } // At this point you might want to do additional checks against different information // in your system, depending on what your needs are. let txArgs = { source: { type: PeerType.VAULT_ACCOUNT, id: userVaultAccountId }, destination: { type: PeerType.ONE_TIME_ADDRESS, oneTimeAddress: { address: to } }, operation: TransactionOperation.TRANSFER, amount: amount, assetId: asset }; try{ let {txId: id} = (await fbks.createTransaction(txArgs)); // Continue monitoring the transaction } catch (e) { fbksError(e); } } // This function is used as a generic error handler for all FireblocksAPI calls, preventing us from duplicating code and // allowing us to easily fix issues in a single location. If a call requires custom handling, its catch clause can // be written without using this function function fbksError(e){ let resp = e.response; if(resp !== 400) { // Handle other errors and return } let respData = resp.data; switch(respData.code){ case 1503: throw new Error("The asset specified is invalid"); case 11001: // In this scenario, since the vault account Id is stored in a local database, we might want to // show a different error or potentially raise an alert, depending on your needs. throw new Error("The vault account Id used is invalid"); default: // If we didn't map the potential error code, it's important to write as much information // about the error as possible, that way we can patch the code with minimal replications or intrusive investigation logUnexpectedError(`Faced error: ${util.inspect(respData,false,null,true)} which is not mapped.`); throw new Error("Unexpected error - please try again later"); } } ``` ```python Python theme={"system"} # Fireblocks SDK initialized beforehand and is defined as the parameter - fbks # There exists some variable which allows us to query a database for information, defined as - db_svc def withdrawal(user_id, asset, amount, to): if not db_svc.user_exists(user_id): raise Exception(f"User does not exist: {user_id}") if not validate_address(to, asset): raise Exception("The address provided does not match conventions for the asset specified.") user_vault_account_id = db_svc.get_vault_account_for_user(user_id) asset_balance = None try: asset_balance = fbks.refresh_vault_asset_balance(user_vault_account_id, asset) except FireblocksApiException as e: fbks_error_handler(e) try: fbks.create_transaction( tx_type=fireblocks_sdk.TRANSACTION_TRANSFER, amount=amount, source=TransferPeerPath(fireblocks_sdk.VAULT_ACCOUNT, user_vault_account_id), destination=DestinationTransferPeerPath(fireblocks_sdk.ONE_TIME_ADDRESS, one_time_address=to) ) except FireblocksApiException as e: fbks_error_handler(e) # This function is used as a generic error handler for all FireblocksAPI calls, preventing us from duplicating code and # allowing us to easily fix issues in a single location. If a call requires custom handling, its catch clause can # be written without using this function def fbks_error_handler(e): if e.error_code == 1503: raise Exception("The asset specified is invalid") elif e.error_code == 11001: # In this scenario, since the vault account Id is stored in a local database, we might want to # show a different error or potentially raise an alert, depending on your needs. raise Exception("The vault account Id used is invalid") else: # If we didn't map the potential error code, it's important to write as much information # about the error as possible, that way we can patch the code with minimal replications or intrusive investigation log_unexpected_error(f"Faced error: {e} which is not mapped.") raise Exception("Unknown error - please try again later") ``` Let's dissect the above code (almost identical for Python); 1. **Lines 4-6**: Performs checks on the user. (This depends on your business logic.) 2. **Lines 7-9**: Performs validation of the `to` address. Using the asset, you'll see the format of the address to expect. You'll have to define this more thoroughly, however, there are libraries that already provide this functionality. 1. Example: BTC SegWit will start with `bc1`, and EVM-based chains will be a 40-character hex (with `0x` prefix and checksummed). You'll have to define this more thoroughly, however, there are libraries that already provide this functionality. 3. **Line 11**: Get the vault account id for the user. Similar to #1 it depends on your business logic and specific setup. 4. **Lines 13-17**: Refresh the balance of the provided information (asset and vault account ID), using the try and catch you catch any exceptions, and send them to the generic API handler (this is also specific to your implementation, the way it's described here might not be the correct way to handle it in your code). If there was an error, with one of the expected, we return some descriptive error message which can be changed to explain to the user what to do. 5. **Lines 22-36**: Build the withdrawal transaction. 6. **Lines 39-43**: Send the transaction. Refer to our generic handler for any error generated from creating the transaction. > [!NOTE] > > **Fixing live error code 11001:** The handling of error code `11001` (invalid vault account) does not directly apply to live scenarios. In this example, the vault account ID is derived from a mock database. In live scenarios, you will need to decide how to fix this yourself. ### 401 - Unauthorized This error, though not common, occurs when a request contains a missing, invalid, or otherwise incorrect JWT, and therefore the transaction fails. Different codes indicate different reasons for the error caused in the JWT. Unless there is a widespread issue with the SDKs themselves, 401 error response codes will only result from: 1. Signing with a different user's private key (e.g. signing with another API user's key instead of yours) 2. Signing with the correct private key, but the incorrect User ID. Both scenarios are not directly code related, and will most likely occur during the development stages of integration or executions of impromptu scripts such as staking. As a result, we cannot provide code samples to address this. [Refer to the API responses page to review codes related to 401 errors for JWT.](/reference/api-error-codes) When encountered, simply validate the API User key and API User secret path (make sure it contains the correct private key). A JWT error code might indicate a critical issue on the production server which you should address immediately. If you encounter such an error during production, do the same on the server that the code is running on. The only other cause of this error is when you do not use the official, unedited Fireblocks SDK (or one of the specific supported side branches). In this instance, modifications to the source code of the Fireblocks SDK caused the error. To address this, you'll need to check code modification and check if a change was made that would yield such an error. Keep in mind, however, that there is no beneficial need to perform changes to the Fireblocks SDK. Therefore, we will not discuss any further details on this matter. ### 403 - Forbidden For specific API calls, such as [get audit logs](/api-reference/audit-logs/get-audit-logs) or [list users](/api-reference/users/list-users), you might receive HTTP status code 403. This is uncommon, since the API currently does not include user-change capabilities and only a small number of operations can trigger 403. If you think you might run into this error, test the code before moving it to production. This makes certain that your API user has the permissions needed. [Refer to the API responses page to review specific 403 errors.](/reference/api-error-codes) ### 404 - Not Found A very common error code, "404 not found", indicates that the page you were looking for does not exist. This error message type states that whatever query you performed, or whatever information you wanted to get, does not exist. How to address such an issue: 1. Identify what's missing - These errors usually happen with GET requests, more than with other HTTP methods, and those GET requests are usually no more than 3 different arguments (with some exceptions), to help you pinpoint which one is "incorrect". 2. Address the missing data by either regenerating it using a new Fireblocks API call or raising an exception/error to notify upstream whoever sent this data. Let's take a look at an example: We provide some code that is invoked by a different component of the system. This code will query a vault account for the number of different assets this wallet contains. If there are more than 10 different assets, `true`, otherwise, this value is `false`. Using the [Find a vault account by ID](/api-reference/vaults/get-a-vault-account-by-id) API reference, you know this specific call uses the vault feature, so you can quickly identify the likely one: * `1004` - No vault account by that Id You can assume this since the response of the API call provides all the data you need, while only needing a single argument - the vault account Id. So, you can identify that this is the most suitable error code. The code: ```javascript JavaScript theme={"system"} // Fireblocks SDK initialized beforehand and is defined as the parameter - fbks // There exists some variable which allows us to query a database for information, defined as - dbSvc async function sufficientAssets(userId){ if(!dbSvc.userExists(userId)){ return new Error(`Unknown user: ${userId}`); } // We assume that the information is stored somewhere you are able to retrieve it, but where it's stored is irrelevant, this is merely for the example let userVaultAccountId = dbSvc.getVaultAccountForUser(userId); try{ let numberOfAssets = (await fbks.getVaultAccountById(userVaultAccountId)).assets.length; return numberOfAssets <= 10; } catch (e) { fbksError(e); } } // This function is used as a generic error handler for all FireblocksAPI calls, preventing us from duplicating code and // allowing us to easily fix issues in a single location. If a call requires custom handling, its catch clause can // be written without using this function function fbksError(e){ let resp = e.response; if(resp !== 400) { // Handle other errors and return } let respData = resp.data; switch(respData.code){ case 1004: throw new UnknownVaultAccountIdError(); default: // If we didn't map the potential error code, it's important to write as much information // about the error as possible, that way we can patch the code with minimal replications or intrusive investigation logUnexpectedError(`Faced error: ${util.inspect(respData,false,null,true)} which is not mapped.`); throw new Error("Unexpected error - please try again later"); } } ``` ```python Python theme={"system"} # Fireblocks SDK initialized beforehand and is defined as the parameter - fbks # There exists some variable which allows us to query a database for information, defined as - db_svc def sufficient_assets(user_id): if not db_svc.user_exists(user_id): raise Exception(f"User does not exist: {user_id}") user_vault_account_id = db_svc.get_vault_account_for_user(user_id) try: asset_count = len(fbks.get_vault_account_by_id(user_vault_account_id)["assets"]) return asset_count <= 10 except FireblocksApiException as e: fbks_error_handler(e) # This function is used as a generic error handler for all FireblocksAPI calls, preventing us from duplicating code and # allowing us to easily fix issues in a single location. If a call requires custom handling, its catch clause can # be written without using this function def fbks_error_handler(e): if e.error_code == 1004: raise UnknownVaultAccountIdException() else: # If we didn't map the potential error code, it's important to write as much information # about the error as possible, that way we can patch the code with minimal replications or intrusive investigation log_unexpected_error(f"Faced error: {e} which is not mapped.") raise Exception("Unknown error - please try again later") ``` Let's dissect the code once more (almost identical for python): 1. Lines 4-6: Check the existence of such a `userId` 2. Line 8: Finds the vault account correlated to this `userId` 1. Let's assume, in this case, that if no such vault account exists in your internal database, you need to add a new entry incrementing from the last added vault account id 3. Lines 9-14: Gets the vault account and counts the number of assets. Returns based on our previous explanation (at most 10). If there is an error, handle using the generic error handler. If there is an error code `1004`, you'll receive a specific type of error. This type of error, in our scenario, will generate a new vault account by something upstream from where the error occurred. *** ## 500 status code A 500 status code indicates that an issue occurred on the server side. Because of this, we cannot provide a way to handle such errors in the same manner as we did for the 4xx errors. We suggest the following: 1. Do not immediately attempt the request again. 2. Double-check the parameters you're passing. One of them might not be formatted correctly, resulting in a failure in our backend. 3. Check the [status page](https://status.fireblocks.com/) to see if there is an ongoing issue. 4. Open a Support ticket or reach out to Support on Slack to address the issue. *** ## Common validations to perform Generally, validations should be done based on your needs and as soon as you have sufficient details to validate them. This will divide into two potential scenarios (but not limited to those two): 1. You received the value and can immediately perform validation on that value 2. You received the value but some additional data is required before performing the validation We provide some common validations that can and should be done which will lower your risk of getting errors in your response that is caused by your code. * Asset validation - When getting an asset, always verify that the asset is indeed a supported one. [More information can be found in the supported assets API reference](/api-reference/blockchains-&-assets/list-assets-legacy). * OTA validation - When using one-time address, which is received from the user themselves, ensure that the format of the address matches the format of the network. * For example, BTC SegWit will require an address starting with `bc1` and complying with Bech32 formatting. EVMs will be a 40-character checksummed hex string with a prefix of `0x`. * Amount validation - In cases where you allow users to specify amounts, such as partial withdrawal uses, you'll always need to: 1. Get the current balance available for the user, either via an API call or via an internal ledger (depending on your business logic). 2. Verify that the amount is a positive decimal value in the range of (0, retrieved balance] (excluding 0). * Vault account validation - Ensure the vault account is a non-negative integer. You might want to add restrictions (both in your Transaction Authorization Policy and in your code, to prevent access to vault accounts you don't want users to be able to access). # Fireblocks Hardhat Plugin Source: https://developers.fireblocks.com/reference/hardhat-plugin Hardhat is a development environment for Ethereum software that provides tools for editing, compiling, debugging, and deploying smart contracts and dApps. The [Fireblocks Hardhat Plugin](https://github.com/fireblocks/hardhat-fireblocks) integrates Fireblocks' secure transaction features into your Hardhat workflow, enabling secure deployment and transaction signing. # Using Hardhat To use the Fireblocks Hardhat Plugin, you must have [Hardhat installed](https://hardhat.org/hardhat-runner/docs/getting-started). You can follow our abridged Hardhat installation steps below or skip to the [Fireblocks Hardhat Plugin](/reference/hardhat-plugin#fireblocks-plugin) section if you already have Hardhat installed. ## Install Hardhat If you need to install Hardhat, you can use our abridged installation guide below. For a complete guide, refer to the official [Hardhat installation guide](https://hardhat.org/hardhat-runner/docs/getting-started). ### Abridged Hardhat installation guide 1. Install the Hardhat package: ```bash theme={"system"} npm install --save-dev hardhat ``` 2. Initialize a new Hardhat project: ```bash theme={"system"} npx hardhat ``` This generates the following Hardhat menu display: ```text theme={"system"} 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 888 888 "88b 888P" d88" 888 888 "88b "88b 888 888 888 .d888888 888 888 888 888 888 .d888888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 👷 Welcome to Hardhat v2.10.1 👷‍ ? What do you want to do? … ❯ Create a JavaScript project Create a TypeScript project Create an empty hardhat.config.js ``` 3. Select **Create a JavaScript project** and press **Enter**. 4. Follow the proceeding setup prompts. Complete these steps to generate a new JavaScript Hardhat project in your current directory. ## Fireblocks Plugin The [Fireblocks Hardhat Plugin](https://github.com/fireblocks/hardhat-fireblocks) helps seamlessly integrate Fireblocks into your Hardhat development stack. You can use it to deploy contracts, sign messages, and send transactions securely through Fireblocks. ### Installing the Hardhat plugin 1. Install the plugin package: ```bash theme={"system"} npm install @fireblocks/hardhat-fireblocks ``` 2. Import the plugin into your `hardhat.config.js` file: ```javascript theme={"system"} require("@fireblocks/hardhat-fireblocks"); const { ApiBaseUrl } = require("@fireblocks/fireblocks-web3-provider"); require('dotenv').config(); ``` ### Configuring the Hardhat plugin This plugin extends the `HttpNetworkUserConfig` object with an optional `fireblocks` field. 1. For this example, copy the code sample below into your `hardhat.config.js` configuration file: ```javascript theme={"system"} module.exports = { solidity: "0.8.17", networks: { sepolia: { url: "https://sepolia.drpc.org", fireblocks: { apiBaseUrl: ApiBaseUrl.Sandbox, // Only use in a Sandbox workspace privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, apiKey: process.env.FIREBLOCKS_API_KEY, vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, } }, }, }; ``` > [!NOTE] > > **Working in Sandbox** > > If you are not using a Sandbox workspace, remove the `apiBaseUrl` line. 2. Delete the `Lock.sol` file that came with your generated Hardhat environment: ```bash theme={"system"} rm contracts/Lock.sol ``` ## Deploy a contract using Hardhat Ignition Now that your Hardhat plugin has been configured to work with Fireblocks, you can begin deploying a smart contract to the Sepolia Ethereum Testnet. ### Create and compile the Solidity file 1. Create a Solidity file in your project folder: ```bash theme={"system"} touch contracts/HelloWorld.sol ``` 2. Copy the following code sample into your `HelloWorld.sol` file: ```solidity theme={"system"} pragma solidity ^0.8.17; contract HelloWorld { string public greet = "Hello World!"; } ``` 3. Run the following command to compile the project: ```bash theme={"system"} npx hardhat compile ``` ### Update and deploy the script 1. Create a new file `ignition/modules/HelloWorld.js` and add the following module definition: ```javascript theme={"system"} const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); module.exports = buildModule("HelloWorld", (m) => { const helloWorld = m.contract("HelloWorld"); return { helloWorld }; }); ``` 2. Deploy your contract to the Ethereum Sepolia Testnet using Hardhat Ignition: ```bash theme={"system"} npx hardhat ignition deploy ignition/modules/HelloWorld.js --network sepolia ``` This should take a couple of minutes to run if everything was configured correctly. Once the deploy script has finished running successfully, the following message will appear: ```bash theme={"system"} Hardhat Ignition 🚀 Deploying [ HelloWorld ] Batch #1 Executed HelloWorld#HelloWorld [ HelloWorld ] successfully deployed 🚀 Deployed Addresses HelloWorld#HelloWorld - ``` ### Post-deployment Hardhat Ignition creates an `ignition/deployments/chain-11155111` folder for Sepolia that contains all the deployment details. This data enables Hardhat Ignition to recover from errors, resume interrupted deployments, and manage deployment states. ## Summary You have now integrated the Fireblocks Hardhat Plugin into your Ethereum development workflow and deployed a smart contract to the Sepolia Testnet using Hardhat Ignition! This setup enables you to securely deploy contracts and manage transactions with Fireblocks’ infrastructure. # Hedera Token Service SDK Source: https://developers.fireblocks.com/reference/hedera-token-service-sdk > [!WARNING] > > **This tool uses Fireblocks Raw Signing** > > Raw Signing is an insecure signing method and is not generally recommended. Bad actors can trick someone into signing a valid transaction message and use it to steal funds. > > For this reason, Raw Signing is a premium feature that requires an additional purchase and is not available in production workspaces by default. If you're interested in this feature and want to see if your use case is eligible for it, please contact your Customer Success Manager. > [!NOTE] > > Fireblocks Sandbox workspaces have Raw Signing enabled by default to allow for testing purposes. This library provides an implementation of the Hedera Client designed for signing operations with the Fireblocks TypeScript SDK. It adheres to [HIP-338](https://hips.hedera.com/#hip-338). Internally, the implementation acts as a wrapper utilizing Raw Signing. It manages all necessary raw signing calls, abstracting the complexities to ensure easy and seamless integration with both Fireblocks and the Hedera SDK. Additionally, this library supports any token operations on the Hedera blockchain that are not yet natively available through the Fireblocks console and APIs. For code examples and further guidance on how to use the library, please visit the [GitHub repository](https://github.com/fireblocks/hbar-fireblocks-sdk). # Internal/External Wallet Objects Source: https://developers.fireblocks.com/reference/internalexternal-wallet-objects ## WalletAsset | Parameter | Type | Description | | - | - | - | | id | string | The ID of the asset | | balance | string | The balance of the wallet | | lockedAmount | string | Locked amount in the wallet | | status | [ConfigChangeRequestStatus](/reference/general-objects#configchangerequeststatus) | Status of the Internal Wallet | | activationTime | string | The time the wallet will be activated if wallet activation is postponed according to the workspace definition | | address | string | The address of the wallet | | tag | string | Used as destination tag for XRP; `memo` for ATOM, EOS, HBAR, LUNA, LUNC, XDB, XEM; `memo_text` for XLM; `notes` for ALGO. | *** ## ExternalWalletAsset | Parameter | Type | Description | | - | - | - | | id | string | The ID of the asset | | status | [ConfigChangeRequestStatus](/reference/general-objects#configchangerequeststatus) | Status of the External Wallet | | activationTime | string | The time the wallet will be activated if wallet activation is postponed according to the workspace definition | | address | string | The address of the wallet | | tag | string | Used as destination tag for XRP; `memo` for ATOM, EOS, HBAR, LUNA, LUNC, XDB, XEM; `memo_text` for XLM; `notes` for ALGO; bank transfer description for fiat providers | *** ## UnmanagedWallet | Parameter | Type | Description | | - | - | - | | id | string | The ID of the Unmanaged Wallet | | name | string | Name of the Wallet Container | | customerRefId | string | \[optional] The ID for AML providers to associate the owner of funds with transactions | | assets | Array of [WalletAsset](#walletasset) | An array of the assets available in the unmanaged wallet | *** ## ExternalWallet | Parameter | Type | Description | | - | - | - | | id | string | The ID of the Unmanaged Wallet | | name | string | Name of the Wallet Container | | customerRefId | string | \[optional] The ID for AML providers to associate the owner of funds with transactions | | assets | Array of [ExternalWalletAsset](#externalwalletasset) | An array of the assets available in the external wallet | # iOS SDK errors Source: https://developers.fireblocks.com/reference/ios-sdk-errors | Error Code | Error Message | | :- | :- | | 100 | Unknown error | | 102 | Failed to download certificates | | 106 | Missing algorithms | | 107 | Missing private keys | | 108 | Failed to upload logs | | 109 | Failed to send version | | 111 | Maximum number of devices per wallet reached | | 200 | Timeout during key creation, didn't get startup message | | 201 | Unknown algorithm | | 202 | Failed to generate key, key exists in server but not on the device | | 203 | Timeout during key creation | | 204 | Failed to send public key | | 205 | Failed to request key | | 206 | Failed to enroll player | | 207 | Failed to create key | | 300 | Failed to request end-user takeover | | 301 | Failed to takeover keys | | 302 | Timeout during key takeover | | 400 | Failed to export keys | | 401 | Missing public keys | | 402 | Missing public key | | 403 | Failed to derive asset key | | 404 | Missing cloud private keys | | 405 | Missing chain code | | 406 | Missing private key | | 407 | Failed to export key, recovered PublicKey is not equal to the original PublicKey | | 408 | Failed to export key | | 500 | Failed to sign transaction, unknown txId | | 501 | Error during transaction signing creation, didn't get start signing message | | 502 | Failed to sign transaction | | 503 | Timeout during transaction signing | | 600 | Backup not available | | 601 | Failed to recover keys | | 603 | The provided passphrase is wrong | | 700 | Failed to get key IDs | | 701 | Missing key IDs in server for backup | | 702 | We have a discrepancy between valid key IDs between server and client | | 703 | Failed to backup keys, missing keys | | 704 | Failed to backup key | | 705 | Invalid passphrase error | | 800 | Invalid add device setup data | | 900 | Failed to join wallet | | 901 | Timeout during join wallet | | 902 | Join wallet was stopped | | 1000 | Failed to approve join wallet | | 1001 | Timeout during approve join wallet | | 1002 | No keys to provision | | 1003 | Approve join wallet was stopped | # Issue New ERC20F Tokens Source: https://developers.fireblocks.com/reference/issue-new-erc-20f-tokens This guide explains how to use the `/v1/tokenization/tokens` endpoint through the SDK to issue a new ERC20F token. This will allow you to deploy and link the token to an asset, enabling easy management of issuance, minting, and burning operations. ## What is ERC20F? ERC20F is a fungible token reference implementation developed by Fireblocks. It builds upon the widely used ERC-20 token standard, with added functionality for upgradability and enhanced control over token operations. This implementation is designed to be flexible, allowing for token minting, burning, and secure integration within Fireblocks' ecosystem. It is optimized for compliance, making it ideal for organizations requiring scalable and customizable token management solutions. It's important to note that the ERC20F token uses a proxy pattern, which decouples the state (in the proxy) from the smart contract logic (referred to as `implementation` in this guide) to support upgradability. Read more about the upgradability features of this contract [here](https://support.fireblocks.io/hc/en-us/articles/14018887816476-Fireblocks-upgradeable-smart-contracts). We built a contract template for ERC20F tokens that you can use to issue new tokens. To find more information about Templates, refer to the [Contract Template guide](/reference/upload-contract-template#what-are-contract-templates). ## Prerequisites Before issuing a new ERC20F token using the Fireblocks SDK, ensure you know the following: 1. **Asset ID:** The `assetId` of the blockchain where the token will be deployed (e.g. ETH\_TEST5 for Sepolia). This is the Fireblocks ID of the gas token of the blockchain where the token will be deployed. (To get the asset ID, refer to this [assetId list](https://support.fireblocks.io/hc/en-us/articles/8993656344092-Supported-blockchain-networks)) 2. **Vault Account:** The `vaultAccountId` that will deploy the token. Ensure the vault has sufficient native gas for transaction fees. You can retrieve the vaultAccountId from the Fireblocks console by checking the URL of the selected vault, e.g., `https://console.fireblocks.io/v2/accounts/vault/`. For more details about Vault Accounts, refer to the [Create Vault Account guide](/reference/create-vault-account). 3. **Contract Template:** Ensure the contract template is uploaded, and you have the `templateId` from the template. for details. (For `ERC20F`, the contract template ID is `41c76f08-3144-4641-96c9-260c8fe846a7` for `Prod US` workspaces). 4. Ensure you have access to the `Fireblocks Smart Contracts package`, which includes the reference protocol contracts. With these in place, you can use the Fireblocks SDK to issue a new token. ## Example: Issuing a New ERC20F Token Here's an example of how to issue a new ERC20F token using the Fireblocks SDK: ### 1. Initialize the Fireblocks SDK First, the Fireblocks SDK and initialize the SDK with your Fireblocks API key and private key: ```javascript theme={"system"} import { Fireblocks, BasePath, } from '@fireblocks/ts-sdk'; const privateKey = '...'; // Your Fireblocks API private key const apiKey = '...'; // Your Fireblocks API key const fireblocksSdk = new Fireblocks({ apiKey, basePath: BasePath.US, // Use BasePath.EU for the EU region secretKey: privateKey, }); ``` ### 2. Retrieve ERC20F Contract Template We need to retrieve the ERC20F contract template using the `getContractTemplate` method: For more information, refer to the [Get Contract Template API reference page](/reference/getcontracttemplatebyid). ```javascript theme={"system"} const template = await fireblocksSdk.contractTemplates.getContractTemplate({contractTemplateId: '00000000-0000-0000-0000-000000000003'}); ``` As part of the response, you'll receive the `implementationContractId` which is required for the next steps. ### 3. Retrieve Implementation Contract Address To get the address of the implementation contract, you need to call the `getDeployedContracts` method: This extra step is required only for templates of contracts that follow the proxy pattern (such as ERC20F). ```javascript theme={"system"} const contractDetails = await fireblocksSdk.deployedContracts.getDeployedContracts({ baseAssetId: 'ETH_TEST5', contractTemplateId: 'YOUR_TEMPLATE_ID_HERE' }) ``` As part of the response, you'll receive the `contractAddress` which is required for the next step. ### 4. Issue New Token To proceed with deploying the new token, you need to define the following deployment variables: * `name`: The token name (e.g., "MyToken"). * `symbol`: The token symbol (e.g., "MTK"). * `defaultAdminAddress`: The address that will receive the DEFAULT\_ADMIN\_ROLE. * `minterAddress`: The address that will receive the MINTER\_ROLE. * `pauserAddress`: The address that will receive the PAUSER\_ROLE. For more information about the roles, refer to the [Roles in Fireblocks smart contracts article](https://support.fireblocks.io/hc/en-us/articles/13926413716892-Roles-in-Fireblocks-smart-contracts). Example: ```javascript theme={"system"} const name = 'MyToken'; const symbol = 'MTK'; const defaultAdminAddress = '0x1234567890123456789012345678901234567890'; const minterAddress = '0x1234567890123456789012345678901234567890'; const pauserAddress = '0x1234567890123456789012345678901234567890'; ``` It's important to recall that the ERC20F token uses a proxy pattern. This means that when deploying the token, you must provide both the implementation contract address and the encoded initialize function in the `constructorParams` field. The proxy will reference the implementation contract and use the initialize method to set up the token parameters. Now let's issue the new token: ```javascript theme={"system"} let deployRequest: CreateTokenRequestDto = { assetId: 'ETH_TEST5', // Fireblocks asset ID for Sepolia network vaultAccountId: '0', // The vault account ID to manage the token createParams: { contractId: '41c76f08-3144-4641-96c9-260c8fe846a7', // The ERC20F contract template ID deployFunctionParams: [ { name: 'implementation', type: 'address', internalType: 'address', value: 'implementationContractAddress', // Address of the implementation contract retrieved in Step 3 }, { "name": "_data", "type": "bytes", "functionValue": { "name": "initialize", "type": "function", "inputs": [ { "name": "_name", "type": "string", "internalType": "string", "value": name // _name (e.g., 'MyToken') }, { "name": "_symbol", "type": "string", "internalType": "string", "value": symbol // _symbol (e.g., 'MTK') }, { "name": "defaultAdmin", "type": "address", "internalType": "address", "value": defaultAdminAddress // Address with DEFAULT_ADMIN_ROLE }, { "name": "minter", "type": "address", "internalType": "address", "value": minterAddress // Address with MINTER_ROLE }, { "name": "pauser", "type": "address", "internalType": "address", "value": pauserAddress // Address with PAUSER_ROLE } ], } } ], }, }; // Issue the token const response = await fireblocksSdk.tokenization.issueNewToken({createTokenRequestDto: deployRequest}); console.log('New token issued:', response); ``` ## Highlights: * Ensure that `contractId` passed in the `createParams` corresponds to the template ID of the contract you want to deploy. In this case, it is the ERC20F contract template ID. * Replace the example addresses with your own values for `defaultAdminAddress`, `minterAddress`, and `pauserAddress`. * Ensure that the vault account has sufficient native gas for transaction fees. * If you need to obtain the ABI for the `initialize` function of the proxy used in step 4, you can find it in the contract template: ```javascript theme={"system"} const implementationTemplate = await fireblocksSdk.contractTemplates.getContractTemplate({contractTemplateId: 'implementationContractId'}); ``` ## Further Reading For more detailed information on API parameters and responses, including examples for using plain HTTP requests or in other programming languages, please have a look at the [Fireblocks API Reference](/reference/issuenewtoken). For more specific guidance on ERC20F contract operations, refer to the [Operating the Upgradable ERC20F Contract Support Center Articles](https://support.fireblocks.io/hc/en-us/articles/13926379966876-Operating-the-Upgradeable-ERC-20F-contract). # Issue new ERC721F/ERC1155F Tokens Source: https://developers.fireblocks.com/reference/issue-new-erc721ferc1155f-tokens This guide explains how to use the `v1/tokenization/collections` endpoint through the Fireblocks SDK to issue new ERC721F/ERC1155F tokens. It will walk you through the process that will result in deploying collection contracts, making them available in the Tokenization Engine. It will also show how to perform minting, burning, and other operations related to their respective standards. ## What are ERC721F and ERC1155F contracts? ERC721F and ERC1155F are non-fungible and semi-fungible token reference implementations developed by Fireblocks. They build upon the widely used ERC-721 and ERC-1155 token standards. ERC721F and ERC1155F tokens can represent ownership of digital or physical assets. They are used in various applications such as digital art, collectibles, and gaming. These implementations are designed to be flexible, allowing for token minting, burning, and secure integration within Fireblocks' ecosystem. They are designed for compliance, making them ideal for organizations requiring scalable and customizable token management solutions. ## Why use Collections API? The Collections API allows you to easily deploy mint, burn and manage non-fungible and semi-fungible tokens. It is a wrapper around the Tokenization API that enables you to handle operations related to ERC721F/ERC1155F tokens. This means the Collections API will deploy and link the NFT contract by leveraging the tokenization API, with additional helpers to manage the NFT metadata. Essentially, it's a convenience layer on top of the Tokenization API that abstracts the complexity of deploying and linking NFT contracts. > [!NOTE] > > Collections API is only available for ERC721F and ERC1155F tokens. ## Prerequisites Before issuing a new ERC721F/ERC1155F token using the Fireblocks SDK, ensure you know the following: 1. assetId: The Asset Id of the blockchain where the token will be deployed (e.g. ETH\_TEST5 for Sepolia). This is the Fireblocks ID of the gas token of the blockchain where the token will be deployed. (To get the asset ID, refer to this [assetId](https://support.fireblocks.io/hc/en-us/articles/8993656344092-Supported-blockchain-networks) list) 2. vaultAccountId: The Id of the Vault that will deploy the token. Ensure the vault has sufficient native gas for transaction fees. The vaultAccountId can be retrieved from the Fireblocks console by checking the URL of the selected vault, e.g., `https://console.fireblocks.io/v2/accounts/vault/`. For more details about Vault Accounts, refer to the Create Vault Account [guide](/reference/create-vault-account). 3. Ensure you have access to the Fireblocks Smart Contracts package, which includes the reference protocol contracts. Contact your CSM if unsure if your workspace has this SKU. ## Example 1: Issuing a New NFT collection Here's an example of how to issue a new ERC721F or ERC1155F token using the Fireblocks SDK: ### 1. Initialize the Fireblocks SDK First, import the Fireblocks SDK and initialize it by providing your Fireblocks API key and private key: ```javascript theme={"system"} import { Fireblocks, BasePath, } from '@fireblocks/ts-sdk'; const privateKey = '...'; // Your Fireblocks API private key const apiKey = '...'; // Your Fireblocks API key const fireblocksSdk = new Fireblocks({ apiKey, basePath: BasePath.US, // Use BasePath.EU for the EU region secretKey: privateKey, }); ``` ### 2. Create a new Collection To deploy a new ERC721F/ERC1155F collection contract, you need to know the following: * `baseAssetId`: The assetId of the blockchain where the collection will be deployed (e.g. ETH\_TEST5 for Sepolia). * `vaultAccountId`: The vaultAccountId of the vault that will deploy the token. * `type`: The type of the collection, either `NON_FUNGIBLE_TOKEN` or `SEMI_FUNGIBLE_TOKEN`. * when type is `NON_FUNGIBLE_TOKEN` the collection will be an ERC721F contract. * when type is `SEMI_FUNGIBLE_TOKEN` the collection will be an ERC1155F contract. * `name`: The name of the collection. This name will be stored on the smart contract. * `symbol`: The symbol of the collection. * `adminAddress`: The address that will receive admin roles on the contract. Ensure that it's an address you control. * `displayName`: The display name of the collection. This name will be displayed in the Fireblocks console. For the purpose of this example, we will create an ERC721F collection on the Ethereum Sepolia network. ```javascript theme={"system"} let collectionRequest: CollectionDeployRequestDto = { baseAssetId: 'ETH_TEST5', vaultAccountId: '0', type: 'NON_FUNGIBLE_TOKEN', name: 'My Collection', symbol: 'MC', adminAddress: '0x5503766D27d1ED4525f5053222E18b29C38eDdB2', displayName: 'My Collection', }; const collection = await fireblocksSdk.tokenization.createCollection({ collectionDeployRequest: collectionRequest, }); ``` This will return a collection object with the following properties: * `id`: The token link id of the collection. Since collections are linked to Tokenization Engine just like fungible tokens deployed using the tokenization API, the collection will have a token link id. * `status`: The token link status. Possible values are `PENDING`, `COMPLETED` and `FAILED` but, right after sending create collection request, status will be `PENDING`. * `type`: The type of the collection, either `NON_FUNGIBLE_TOKEN` or `SEMI_FUNGIBLE_TOKEN`. * `displayName`: The display name of the collection as it appears in the Fireblocks console. * `tokenMetadata`: The metadata of the collection. For now, it will be empty. But once the deployment transaction is completed, and the collection is linked, the metadata will be updated with the contract address and other details. For more information, refer to the [Collection Metadata](/reference/issue-new-erc721ferc1155f-tokens#collection-metadata) section below. > [!NOTE] > > The procedure for creating an ERC1155F collection is the same, only that the type should be changed to `SEMI_FUNGIBLE_TOKEN`. #### Collection Metadata: The token metadata can be one of the following: * `ContractMetadataDto`: If the contract is a utility contract (i.e. any arbitrary contract). * `AssetMetadataDto`: If the contract is a fungible token (i.e. ERC20). * `CollectionMetadataDto`: If the contract is a non-fungible token (i.e. ERC721 or ERC1155). Once the contract is deployed and the collection is linked. The `tokenMetadata` will assume a value of type `collectionMetadataDto`. This metadata object that holds the contract address and other details. This object will be stored on the Fireblocks database and it looks like this: ```json theme={"system"} { name: "My Collection", symbol: "MC", standard: "ERC721F", blockchainDescriptor: "ETH", contractAddress: "0x5503766D27d1ED4525f5053222E18b29C38eDdB2", } ``` Now that we have a token link with a pending status, we can poll the status of the token link to check if the collection is deployed and the linkage is complete. To do that we can use the `getLinkedCollections` method. #### 2.1 Fetching a Collection To fetch a collection, you need to know the id of the collection, which is the token link id of the linked token. In this example we will use the id of the collection we created in the previous step. If the id is not known, you can use the getLinkedCollections method, which will return a list of all linked collections. Then you can find and use the id of the collection you want to fetch. ```javascript theme={"system"} const collectionId = collection.id; // collection object from the previous step const fetchedCollection = await fireblocksSdk.tokenization.getCollectionById({ TokenIdParamDto: collectionId }); This will return a collection object with the same properties as mentioned in the previous step. ``` If you want to continue to mint an NFT, you can proceed to the next example below. ## Example 2: Minting an NFT To mint an NFT, the caller must have the `MINTER_ROLE` role on the collection contract. If the collection was deployed through Collections API, the `MINTER_ROLE` role is assigned to the adminAddress that was provided when creating the collection. You also need to perform two steps mentioned in Issuing a New NFT collection example above. > [!NOTE] > > If you wish to use a different address to mint tokens, you can assign the `MINTER_ROLE` role to the desired address. The process is similar to the one described in the [Assigning Roles](/reference/setting-up-roles-in-erc20f-tokens#3-grant-role-to-address) guide for ERC20F. ### 3. Minting an NFT To mint an NFT, you need to know the following: * `vaultAccountId`: The vaultAccountId that will mint the token. Make sure this vault has enough native gas tokens for the transaction fees and the MINTER\_ROLE role on the smart contract. * `to`: The address that will receive the minted token. * `tokenId`: The id of the token to mint. This id must be unique within the collection. For this field, it is recommended to have a numerical format and in sequential order. * `amount`: (Optional) The amount of the token to mint. For ERC721F, amount is ignored. For ERC1155F, the amount should be 1 or greater. * `metadataURI`: (Optional) The metadata URI of the token. This URI will be stored on the smart contract and can be used to fetch the metadata of the token. This URL is usually an IPFS URL. * `metadata`: (Optional) The metadata of the token. This metadata object will be uploaded to IPFS and the URI will be stored on the smart contract. For detailed information about the metadata, refer to the [NFT metadata](/reference/issue-new-erc721ferc1155f-tokens#nft-metadata) section below. #### NFT Metadata: The metadata object should have the following properties: * `name`: The name of the token. * `description`: The description of the token. * `image`: (Optional) The image of the token. This should be a URL to the image of the token. (e.g. [https://some\\\_domain.com/image\\\_filepath](https://some\\_domain.com/image\\_filepath)). * `animation`: (Optional) The animation of the token. This should be a URL to the animation of the token. (e.g. [https://some\\\_domain.com/animation\\\_filepath](https://some\\_domain.com/animation\\_filepath)). * `external_url`: (Optional) The external URL of the token. This should be a URL to the token's external page. (e.g. [https://some\\\_domain.com/token\\\_page](https://some\\_domain.com/token\\_page)). * `attributes`: (Optional) The attributes of the token. The token can have many attributes or traits depending on the use case. This should be an array of objects, each object should have the following properties: * `trait_type`: The trait type of the attribute. (e.g. rarity). * `value`: The value of the attribute. (e.g. common, legendary). * `display_type`: (Optional) The display type of the attribute. This describes how would you like the trait to be displayed (e.g. number, date). Here is an example of a metadata object: ```json theme={"system"} { name: "My Token", description: "This is my token", image: "https\://some_domain.com/image_filepath", animation: "https\://some_domain.com/animation_filepath", external_url: "https\://some_domain.com/token_page", attributes: [ { trait_type: "rarity", value: "common", display_type: "string" }, { trait_type: "level", value: 1, display_type: "number" } ] } ``` **What if I don't want to upload images and animations to a hosted server?** If you don't want to upload images and animations to a hosted server, you can encode the image and animation files to base64 and pass them in the metadata object. Fireblocks will upload the metadata object containing base64 content to IPFS. This can be done as follows for an image in PNG format: ```javascript theme={"system"} const image = fs.readFileSync('path/to/fileimage.png').toString('base64'); const metadata = { name: "My Token", description: "This is my token", image: `data:image/png;base64,${image}`, attributes: [ { trait_type: "rarity", value: "common", display_type: "string" }, { trait_type: "level", value: 1, display_type: "number" } ] } ``` Once you have the metadata object, you can pass it to the metadata field in the mint request. For this example, we assume that the metadata is already uploaded to IPFS and we have the URI of the metadata. We are also minting an ERC721F token, so we will not use the amount field. If you are minting an ERC1155F token, you can set the amount field to the desired amount. > [!WARNING] > > Make sure to use either `metadataURI` or `metadata` - not both. If you use both metadataURI and metadata the request will fail. ```javascript theme={"system"} let mintRequest: CollectionMintRequestDto = { vaultAccountId: 'vaultAccountId', to: '0x5503766D27d1ED4525f5053222E18b29C38eDdB2', tokenId: 1, metadataURI: 'ipfs://QmP4P6f7mDHzikhdwLBVSCxCPEgmjwcWSVBHbtSyfBYzBC', }; const collectionId: CollectionIdParamDto = { id: collection.id }; // collection object from the previous step const txId = await fireblocksSdk.tokenization.mintCollectionToken({ colletionId, collectionMintRequest: mintRequest, }); ``` This will return the Fireblocks transaction id of the Mint transaction. #### 3.1. Fetching the minted token To fetch the minted token, you need to know the tokenId of the token. In this example we will use the tokenId we minted in the previous step. To fetch the token you can use the fetchCollectionTokenDetails method that will return the nft token object. ```javascript theme={"system"} const tokenId = mintRequest.tokenId; // tokenId from the mint request const fetchedToken = await fireblocksSdk.tokenization.fetchCollectionTokenDetails(collectionId, tokenId); ``` This will return a token object with the following properties: * `tokenId`: The id of the token. * `metadataURI`: The metadata URI of the token. * `totalSupply`: The total supply of the token. If you would like to continue learn how to burn NFT, you can proceed to the next example. ## Example 3: Burning an NFT To burn an NFT, the caller must have the `BURNER_ROLE` role on the collection contract. The `BURNER_ROLE` role is not assigned to anyone by default. You also need to make sure you have a collection of NFTs and an NFT to burn which described in example 1 and 2. To assign the `BURNER_ROLE` role to an address, you can use the `grantRole` method. For more information, refer to the [Assigning Roles](/reference/setting-up-roles-in-erc20f-tokens#3-grant-role-to-address) guide for ERC20F. The process is the same for ERC721F/ERC1155F. ### 4. Burning an NFT To burn an NFT, you need to know the following: * `collectionId`: The id of the collection. * `vaultAccountId`: The vaultAccountId that will burn the token. Make sure this vault has enough native gas for the transaction fees and the BURNER\_ROLE role on the smart contract. * `tokenId`: The id of the token to burn. * `amount`: (Optional) The amount of the token to burn. For ERC721F, amount is ignored. For ERC1155F, amount should be 1 or greater. For this example, we will burn an ERC721F token, so we will not use the amount field. If you are burning an ERC1155F token, you can set the amount field to the desired amount. ```javascript theme={"system"} let burnRequest: CollectionBurnRequestDto = { vaultAccountId: 'vaultAccountId', tokenId: 1, }; let collectionId = { id: collection.id }: CollectionIdParamDto; // collection object from the previous step const txId = await fireblocksSdk.tokenization.burnCollectionToken({ colletionId, collectionBurnRequestDto: burnRequest, }); ``` This will return the Fireblocks transaction id of the Burn transaction. ## Summary The Collections API is a high-level API provides a simple interface to manage NFTs. Without collections API, you would need to: 1. Fetch the templates from `getContractTemplates`. 2. Find the template (e.g. ERC721F) from the list. 3. Fetch the template using `getContractTemplate`. 4. Deploy the contract. 5. Mint the NFT. With collections API all of that is abstracted into the purpose-driven methods explained in this guide. ## Further Reading For more detailed information on API parameters and responses, including examples for using plain HTTP requests or in other programming languages, please have a look at the [Fireblocks API Reference](/reference/createnewcollection). For more specific guidance on ERC721F and ERC1155F contracts operations, refer to the \[Operating the Upgradable ERC721F Contract Support Center Articles]\(Operating the Upgradable ERC721F Contract Support Center Articles) guides. # Java SDK Source: https://developers.fireblocks.com/reference/java-sdk > [!NOTE] > > **Install the Fireblocks Documentation MCP first.** Whether you're using an AI assistant or not, this makes it easy to search, reference, and keep your Fireblocks work grounded in the official docs. [Set up your AI agent](/docs/building-with-ai#fireblocks-documentation-mcp) with one copied prompt, or follow the manual steps in [Getting Started](/docs/quickstart). Java developers can use [the official Fireblocks Java SDK](https://github.com/fireblocks/java-sdk) to interact with the Fireblocks API. Instructions for setup and usage can be found in the repo's README.md file. # Using the Fireblocks Java SDK Building the API client library requires: 1. Java 11+ 2. Maven/Gradle ## Installation ### Maven users Add this dependency to your project's POM: ```xml theme={"system"} com.fireblocks.sdk fireblocks-sdk 33.0.0 ``` ### Gradle users Add this dependency to your project's build file: ```groovy theme={"system"} implementation 'com.fireblocks.sdk:fireblocks-sdk:33.0.0' ``` Check the [Java SDK releases](https://github.com/fireblocks/java-sdk/releases) for the latest version. ## Getting Started ### Initiate Fireblocks Client You can initialize the Fireblocks SDK in two ways: either by setting environment variables or by providing the parameters directly: **Using Environment Variables** You can initialize the SDK using environment variables from your .env file or by setting them programmatically. > [!NOTE] > > **Use the correct API Base URL** > > In the following script, make sure you're using the correct value for `FIREBLOCKS_BASE_PATH` for your environment: > > * For Sandbox workspaces: `https://sandbox-api.fireblocks.io` > * For US Mainnet or Testnet workspaces: `https://api.fireblocks.io` > * For EU Mainnet or Testnet workspaces: `https://eu-api.fireblocks.io` > * For EU2 Mainnet or Testnet workspaces: `https://eu2-api.fireblocks.io` > > [Learn more about workspace differences](/docs/workspace-environments). Use bash commands to set environment variables: ```bash theme={"system"} export FIREBLOCKS_BASE_PATH="https://sandbox-api.fireblocks.io/v1" export FIREBLOCKS_API_KEY="my-api-key" export FIREBLOCKS_SECRET_KEY="my-secret-key" ``` Execute the following Java code: ```java theme={"system"} import com.fireblocks.sdk.Fireblocks; import com.fireblocks.sdk.ConfigurationOptions; ConfigurationOptions configurationOptions = new ConfigurationOptions(); Fireblocks fireblocks = new Fireblocks(configurationOptions); ``` **Providing Local Variables** Alternatively, you can directly pass the required parameters when initializing the Fireblocks API instance: ```java theme={"system"} import com.fireblocks.sdk.BasePath; import com.fireblocks.sdk.Fireblocks; import com.fireblocks.sdk.ConfigurationOptions; ConfigurationOptions configurationOptions = new ConfigurationOptions() .basePath(BasePath.Sandbox) .apiKey("my-api-key") .secretKey("my-secret-key"); Fireblocks fireblocks = new Fireblocks(configurationOptions); ``` ## Examples ### Creating a Vault Account ```java theme={"system"} CreateVaultAccountRequest request = new CreateVaultAccountRequest().name("My Vault Account"); String idempotencyKey = UUID.randomUUID().toString(); // A unique identifier for the request. If the request is sent multiple times with the same idempotency key, the server will return the same response as the first request. The idempotency key is valid for 24 hours. // Async request CompletableFuture> response = fireblocks.vaults().createVaultAccount(request, idempotencyKey); // Waiting for the request VaultAccount vaultAccount = response.get().getData(); ``` ### Retrieving Vault Accounts ```java theme={"system"} BigDecimal limit = new BigDecimal(10); CompletableFuture> response = fireblocks.vaults().getPagedVaultAccounts(null, null, null, null, null, null, null, limit, null, null, null); VaultAccountsPagedResponse vaultAccountsPagedResponse = response.get().getData(); ``` ### Creating a Transaction ```java theme={"system"} TransactionRequest transactionRequest = new TransactionRequest() .operation(TransactionOperation.TRANSFER) .source(new SourceTransferPeerPath() .type(TransferPeerPathType.VAULT_ACCOUNT) .id("0")) .destination(new DestinationTransferPeerPath() .type(TransferPeerPathType.VAULT_ACCOUNT) .id("1")) .amount(new TransactionRequestAmount("0.001")) .assetId("ETH_TEST") .note("My first Java transaction!") .externalTxId(""); // Optional - Your unique ID; Fireblocks rejects later requests with the same value // Use externalTxId, not an idempotency key, for transactions: an idempotency key expires after 24 hours CompletableFuture> response = fireblocks.transactions().createTransaction(transactionRequest, null, null); CreateTransactionResponse transactionResponse = response.get().getData(); String txId = transactionResponse.getId(); ``` # JavaScript SDK errors Source: https://developers.fireblocks.com/reference/javascript-sdk-errors | Error Code | Error Message | | :- | :- | | 100 | UNKNOWN\_ERROR | | 101 | PROCESS\_ALREADY\_RUNNING | | 102 | INVALID\_CERTIFICATE | | 103 | INVALID\_MPC\_MESSAGE | | 104 | FAILED\_MPC\_BROADCAST | | 105 | UNEXPECTED\_MPC\_MESSAGE | | 106 | MISSING\_ALGORITHMS | | 107 | MISSING\_PRIVATE\_KEYS | | 108 | INCOMPLETE\_DEVICE | | 109 | INSTANCE\_ALREADY\_INITIALIZED | | 110 | INVALID\_PHYSICAL\_DEVICE\_ID | | 111 | MAX\_DEVICES\_PER\_WALLET\_REACHED | | 112 | UNKNOWN\_RESPONSE\_FROM\_MESSAGEHANDLER | | 113 | API\_CALL\_FAILED | | 200 | NO\_FIRST\_MESSAGE\_TIMEOUT | | 201 | UNKNOWN\_ALGORITHM | | 202 | MISSING\_KEY\_ON\_DEVICE | | 203 | KEY\_CREATION\_TIMEOUT | | 204 | FAILED\_TO\_SEND\_PUBLIC\_KEY | | 205 | FAILED\_TO\_REQUEST\_KEY | | 206 | FAILED\_TO\_ENROLL\_PLAYER | | 207 | FAILED\_TO\_CREATE\_KEY | | 208 | FAILED\_TO\_CONFIRM\_KEY | | 300 | FAILED\_TO\_REQUEST\_END\_USER\_TAKEOVER | | 301 | FAILED\_TO\_TAKEOVER\_KEYS | | 302 | KEY\_TAKEOVER\_TIMEOUT | | 400 | FAILED\_TO\_EXPORT\_KEYS | | 401 | MISSING\_PUBLIC\_KEYS | | 402 | MISSING\_PUBLIC\_KEY | | 403 | FAILED\_TO\_DERIVE\_ASSET\_KEY | | 404 | MISSING\_CLOUD\_PRIVATE\_KEYS | | 405 | MISSING\_CHAIN\_CODE | | 406 | MISSING\_PRIVATE\_KEY | | 407 | PUBLIC\_KEYS\_DISCREPANCY\_ERROR | | 408 | FAILED\_TO\_EXPORT\_KEY | | 500 | UNKNOWN\_TX\_ID | | 501 | NO\_FIRST\_TX\_SIGNING\_MESSAGE | | 502 | FAILED\_TO\_SIGN\_TRANSACTION | | 503 | TRANSACTION\_SIGNING\_TIMEOUT | | 504 | SIGNING\_STOPPED | | 505 | TRANSACTION\_STATUS\_FAILURE | | 600 | BACKUP\_NOT\_AVAILABLE\_ERROR | | 601 | FAILED\_TO\_RECOVER\_KEYS | | 602 | UNKNOWN\_BACKUP\_ALGORITHM | | 603 | WRONG\_RECOVERY\_PASSPHRASE | | 700 | FAILED\_TO\_GET\_KEY\_IDS | | 701 | MISSING\_KEY\_IDS\_FOR\_BACKUP | | 702 | KEYS\_DISCREPANCY\_BACKUP\_ERROR | | 703 | MISSING\_KEYS\_BACKUP\_ERROR | | 704 | FAILED\_TO\_BACKUP\_KEY | | 705 | INVALID\_PASSPHRASE\_ERROR | | 706 | INCOMPLETE\_BACKUP | | 800 | INVALID\_ADD\_DEVICE\_SETUP\_DATA | | 801 | MPC\_SETUP\_ALREADY\_STARTED | | 900 | FAILED\_TO\_JOIN\_WALLET | | 901 | JOIN\_WALLET\_TIMEOUT | | 902 | JOIN\_WALLET\_STOPPED | | 1000 | FAILED\_TO\_APPROVE\_JOIN\_WALLET | | 1001 | APPROVE\_JOIN\_WALLET\_TIMEOUT | | 1002 | NO\_KEYS\_TO\_PROVISION | | 1003 | APPROVE\_JOIN\_WALLET\_STOPPED | # JS SDK (Legacy) Source: https://developers.fireblocks.com/reference/js-sdk-legacy > [!WARNING] > > **Deprecation notice** > > The legacy JS SDK (`fireblocks-sdk`) will reach end of life on November 1, 2026, after which it will no longer receive updates, bug fixes or support. Existing installations will continue to work and the package will not be unpublished, but we cannot guarantee compatibility with future Fireblocks API changes. Fireblocks is not responsible for any disruption resulting from continued use of the legacy SDK after this date. Migrate to the `@fireblocks/ts-sdk` package now. For upgrade steps, see the [SDK Migration Guide](/reference/sdk-migration-guide). A JavaScript or Typescript developer can use the Fireblocks API in 2 different ways: 1. [The Fireblocks JavaScript SDK.](https://github.com/fireblocks/fireblocks-sdk-js) 2. A standard HTTP library, such as [Axios.](https://axios-http.com/docs/intro) In this guide, you'll set up the Fireblocks SDK and see an example of non-SDK usage (including signing the JWT token). Additionally if you are developing on EVM chains - you might be using some of the familiar libraries, such as `web3.js` or `ethers.js` - Fireblocks is well intergrated into these libraries as described in the [Ethereum Development](/reference/evm-web3-provider). # Using the Fireblocks SDK ## Install Node v16 or newer The Fireblocks JavaScript SDK requires **Node v16 or newer**. You can check which version of Node you already have installed by running the following command. `node -v` [Learn how to install or update Node to a newer version.](https://nodejs.org/en/download/) ## Install fireblocks-sdk The Fireblocks JavaScript SDK is open-source and hosted on both GitHub and PIP, the official package repository. * **Source code:** [https://github.com/fireblocks/fireblocks-sdk-js](https://github.com/fireblocks/fireblocks-sdk-js) * **JavaScript Package:** [https://www.npmjs.com/package/fireblocks-sdk](https://www.npmjs.com/package/fireblocks-sdk) Installing the latest SDK is easy with `npm`: `npm install fireblocks-sdk` ## Your First Fireblocks JavaScript script! Now that you're set-up, run a quick check for the API. The script will query existing vaults, create a new vault and then query again to see that the vaults were created. This Axios-based implementation will require some library dependencies: * [Axios](https://www.npmjs.com/package/axios) * [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) > [!NOTE] > > **Use the correct API Base URL** > > In the following script, make sure you're using the correct value for `baseUrl` for your environment: > > * For Sandbox workspaces: `https://sandbox-api.fireblocks.io` > * For Mainnet or Testnet workspaces: `https://api.fireblocks.io` > > [Learn more about workspace differences](/docs/workspace-environments). ```javascript theme={"system"} const fs = require('fs'); const path = require('path'); const { FireblocksSDK } = require('fireblocks-sdk'); const { exit } = require('process'); const { inspect } = require('util'); const apiSecret = fs.readFileSync(path.resolve("``"), "utf8"); const apiKey = "" // Choose the right api url for your workspace type const baseUrl = "https://sandbox-api.fireblocks.io"; const fireblocks = new FireblocksSDK(apiSecret, apiKey, baseUrl); (async () => { // Print vaults before creation let vaultAccounts = await fireblocks.getVaultAccountsWithPageInfo({}); console.log(inspect(vaultAccounts, false, null, true)); // Create vault account const vaultCreation = await fireblocks.createVaultAccount("QuickStart_Vault"); console.log(inspect(vaultCreation, false, null, true)); // Print vaults after creation vaultAccounts = await fireblocks.getVaultAccountsWithPageInfo({}); console.log(inspect(vaultAccounts, false, null, true)); })().catch((e)=>{ console.error(`Failed: ${e}`); exit(-1); }) ``` ```javascript theme={"system"} const fs = require('fs'); const path = require('path'); const { FireblocksSDK } = require('fireblocks-sdk'); const { exit } = require('process'); const { inspect } = require('util'); const axios = require('axios'); const { sign } = require('jsonwebtoken'); const { v4: uuid } = require('uuid'); const crypto = require('crypto'); const apiSecret = fs.readFileSync(path.resolve("``"), "utf8"); const apiKey = "" // Choose the right api url for your workspace type const baseUrl = "https://sandbox-api.fireblocks.io"; const fireblocks = new FireblocksSDK(apiSecret, apiKey, baseUrl); class FireblocksRequestHandler{ baseUrl; apiSecret; apiKey; constructor(apiSecret, apiKey, baseUrl = "https://api.fireblocks.io"){ this.baseUrl = baseUrl; this.apiSecret = apiSecret; this.apiKey = apiKey; } async post(path, data){ const jwt = this.jwtSign(path,data); return (await this.req(jwt, path, data, "POST")); } async get(path) { const jwt = this.jwtSign(path); return (await this.req(jwt, path, undefined, "GET")); } async req(jwt, path, data, method){ const resp = await axios({ url: `${this.baseUrl}${path}`, method: method, data, headers:{ "X-API-Key":this.apiKey, "Authorization": `Bearer ${jwt}` } }); return resp.data; } jwtSign(path, data){ const token = sign({ uri: path, nonce: uuid(), iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 55, sub: this.apiKey, bodyHash: crypto.createHash("sha256").update(JSON.stringify(data || "")).digest().toString("hex") }, this.apiSecret, {algorithm: "RS256"}); return token; } } (async () => { const requestHandler = new FireblocksRequestHandler(apiSecret, apiKey); // Print vaults before creation let vaults = await requestHandler.get("/v1/vault/accounts_paged"); console.log(inspect(vaults, false, null, true)); // Create vault account const vaultCreation = await requestHandler.post("/v1/vault/accounts", {"name":"QuickStart_Vault"}); console.log(inspect(vaultCreation, false, null, true)); // Print vaults after creation vaults = await requestHandler.get("/v1/vault/accounts_paged"); console.log(inspect(vaults, false, null, true)); })().catch((e)=>{ console.error(`Failed: ${e}`); exit(-1); }); ``` # Key Link Supported Key Algorithms Source: https://developers.fireblocks.com/reference/keylink-key-algorithms The key algorithms and public key formats that Fireblocks Key Link accepts for validation keys and signing keys, and how the two key types work together. Fireblocks Key Link uses two types of external keys that you register in your workspace: validation keys and signing keys. Each key type supports a different set of algorithms. Validation keys accept a wider range of algorithms because they only certify signing keys and never sign transactions. | Key type | Supported algorithms | Purpose | | - | - | - | | Validation key | RSA (2048-bit or larger), ECDSA P-256, ECDSA secp256k1, EdDSA Ed25519 | Signs the certificate of each signing key you register, proving that you control it | | Signing key | ECDSA secp256k1, EdDSA Ed25519 | Signs transactions for the vault account it is assigned to | ## Validation keys A validation key is a public key that you register with Fireblocks to certify your signing keys. When you add a new signing key, you submit a certificate for it that is signed by one of your validation keys. Fireblocks accepts the signing key only when that certificate validates against a validation key registered in your workspace. ### Supported validation key algorithms You can register a validation key that uses any of these algorithms: * RSA, with a key size of 2048 bits or larger * Elliptic Curve Digital Signature Algorithm (ECDSA) on the P-256 curve * ECDSA on the secp256k1 curve * Edwards-curve Digital Signature Algorithm (EdDSA) on the Ed25519 curve Any supported validation key algorithm can sign a certificate for a signing key of either supported type. For example, an RSA validation key can certify both ECDSA secp256k1 and EdDSA Ed25519 signing keys. ### Public key format Submit the validation key's public key as a PEM-encoded SubjectPublicKeyInfo structure. This format starts with the following header: ```text theme={"system"} -----BEGIN PUBLIC KEY----- ``` The Console rejects RSA public keys in the PKCS#1 format, which start with `-----BEGIN RSA PUBLIC KEY-----`. If your HSM exports RSA public keys in PKCS#1 format, convert the key before you register it. For example, with OpenSSL: ```bash theme={"system"} openssl rsa -RSAPublicKey_in -in validation-key-pkcs1.pem -pubout -out validation-key.pem ``` ### Expiration Each validation key has an expiration period, set with the `daysTillExpired` parameter when you add the key. The minimum value is 1 day. After a validation key expires, you can no longer use it to certify new signing keys. Register a new validation key before the current one expires so you can keep adding signing keys. New validation keys require approval from your workspace's admin quorum before they become active. For the request and response fields, see [Add a new validation key](/api-reference/key-link-beta/add-a-new-validation-key). ## Signing keys A signing key is the key in your HSM or key management system that signs transactions for a vault account. Signing keys must use one of these algorithms: * ECDSA on the secp256k1 curve (`ECDSA_SECP256K1`) * EdDSA on the Ed25519 curve (`EDDSA_ED25519`) The algorithm determines which blockchains the key can sign for. For the request fields, including the signed certificate (`signedCertPem`), see [Add a new signing key](/api-reference/key-link-beta/add-a-new-signing-key). ## Related pages * [Key Link transaction metadata](/reference/keylink-transaction-metadata) * [Get list of registered validation keys](/api-reference/key-link-beta/get-list-of-registered-validation-keys) * [Get list of signing keys](/api-reference/key-link-beta/get-list-of-signing-keys) # Key Link Key Status in the API and Console Source: https://developers.fireblocks.com/reference/keylink-key-status How the Fireblocks Key Link API fields for signing keys and validation keys map to the key status shown in the Console, and when a validation key can certify signing keys. The Fireblocks Console shows a status for each Key Link signing key and validation key. The Key Link API does not return that status as a single field. Instead, the Console derives it from several fields in the API response. Use the tables on this page to compute the same status from the API so your systems and the Console agree. ## Signing key status A signing key is the key in your HSM or key management system that signs transactions for a vault account. Its Console status depends on three fields returned by [Get list of signing keys](/api-reference/key-link-beta/get-list-of-signing-keys): `enabled`, `vaultAccountId`, and `agentUserId`. | `enabled` | `vaultAccountId` | `agentUserId` | Console status | Meaning | | - | - | - | - | - | | `false` | `null` | Set | Pending proof | The key is registered and linked to an Agent user, and is waiting for proof of ownership. | | `true` | `null` | Set | Pending assignment | Proof of ownership succeeded. The key is not assigned to a vault account and controls no addresses. | | `true` | Set | Set | Active | The key is assigned to a vault account and is used for signing. | Every signing key is linked to an Agent user when you create it, so `agentUserId` is always set. After a signing key becomes enabled, it never returns to `enabled: false`. ## Validation key status A validation key certifies the signing keys you register: each new signing key's certificate must be signed by one of your validation keys. Its Console status depends on the `enabled`, `revoked`, and `expirationTime` fields returned by [Get list of registered validation keys](/api-reference/key-link-beta/get-list-of-registered-validation-keys), and on the key's approval request. | API fields | Console status | | - | - | | `expirationTime` is in the past | Expired | | `enabled: true` and not expired | Active | | `enabled: false`, waiting for admin quorum approval | Pending approval | | `enabled: false`, approval rejected | Rejected | | `revoked: true` | Not listed in the Console | The approval state is not part of the API response. The Console reads it from the approval request created when the validation key was added to your workspace. When you only have the API response, a validation key with `enabled: false` that is not revoked is either pending approval or rejected. To include revoked validation keys in the list, set the `includeRevoked` query parameter to `true`. ### When a validation key can certify signing keys A validation key can certify new signing keys only when all of these conditions are true: * `enabled` is `true` * `revoked` is `false` * The key has not expired (`expirationTime` is in the future) ### Revoke a validation key Revoke a validation key when you no longer want it to certify new signing keys, for example when the key is retired or compromised. > [!WARNING] > > Revoking a validation key is permanent. You cannot re-enable a revoked validation key. To certify new signing keys afterward, add a different validation key. To revoke a validation key in the Console: 1. Go to **Settings** > **External Keys** > **Validation keys**. 2. Find the key you want to revoke, then select **Revoke key**. To revoke a validation key with the API, use [Disables a validation key](/api-reference/key-link-beta/disables-a-validation-key). ## Related pages * [Key Link transaction metadata](/reference/keylink-transaction-metadata) # Key Link transaction metadata Source: https://developers.fireblocks.com/reference/keylink-transaction-metadata When Fireblocks Key Link sends a `KEY_LINK_TX_SIGN_REQUEST` message to your Fireblocks Agent, the request carries the transaction details, mainly in the `metadata` field. Fireblocks Agent forwards this message to your customer server (the `/messagesToSign` endpoint you implement) exactly as received, so your server can inspect these details (for example, to confirm the amount and destination match what you expect) before authorizing your HSM or key management system to sign. > [!NOTE] > > **Parse this structure defensively** > > The fields on this page are not an exhaustive list. Requests can contain fields that aren't documented here, and Fireblocks can add fields at any time. An unknown or undocumented field must never break your integration. > > `metadata` does not have a fixed, versioned schema. The same field name can carry different values at different nesting levels of the same request; this page calls those cases out. > > If your integration reads these fields: > > * Ignore fields you don't recognize instead of failing, and don't reject a request because it contains one. > * Don't assume that a field marked optional or conditional is always present, or that its type stays the same across transaction types. > * Don't hard-fail your signing flow solely because a field is missing, unless you've deliberately decided to require it. ## Stability The core fields are stable: the transaction ID, the asset, the amounts, the source and destination, and the policy verdict. Other fields are informational, and Fireblocks may add new ones over time. Base your signing decisions on the signed `txMetaData` object, which Fireblocks Agent verifies before forwarding the request, rather than on the unsigned flat fields in `metadata`. The flat fields are convenience copies and aren't covered by any signature. ## Where this appears A `KEY_LINK_TX_SIGN_REQUEST` you receive at `/messagesToSign` is a `MessageEnvelope`, where `message.payload` is a stringified JSON object. Parsing it gives you a `MessagePayload`: ```json theme={"system"} { "tenantId": "3827c4de-972e-5641-8a51-0a9f713aa708", "txId": "50076a36-4580-4354-b8ea-b8c3e3e85479", "keyId": "ae060113-45f3-46bd-9528-aa8a246cf04b", "algorithm": "EDDSA_ED25519", "type": "KEY_LINK_TX_SIGN_REQUEST", "signingDeviceKeyId": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", "timestamp": 1789404629, "messagesToSign": [{ "message": "0201030766...", "index": 0 }], "metadata": { "...": "see below" } } ``` Transaction signing payloads don't include a `version` field. **timestamp** `number` The time the signing request was created, as a Unix timestamp in seconds. ### `metadata` A flat object mixing a transaction summary with lower-level signing details: **txMetaData** `string` A JSON-encoded string. Parse it to get the [TxMetaData](#txmetadata) object described below. **txMetaDataSignatures** `array` Signatures over the `txMetaData` string. Each entry: **properties** **id** `string` The signer's identifier. `policy_service` for the standard signature; can also be a user ID when `type` is `USER_AUTHORIZER`. **type** `string` `SERVICE` (signed by Fireblocks) or `USER_AUTHORIZER` (signed by a designated approving user, e.g. under a 2-tier policy). **signature** `string` A hex-encoded RSA-SHA256 signature of the `txMetaData` string. **signInfo** `array` The raw payloads to be signed, prior to their appearance in `messagesToSign`. A request can contain multiple entries, for example when the transaction needs signatures from additional signer paths. **properties** **payload** `string` Hex-encoded raw bytes of the message to sign. **path** `array` BIP32-style derivation path segments. Empty (`[]`) for assets that don't need one. **rawTx** (Optional) The raw transaction. Present when additional callback parameters are enabled for your workspace. Can be empty for some blockchains. **useKeccak** (Optional) A signing flag. Entries can carry signing flags such as `useKeccak`, depending on the blockchain. **signingCount** `number` (Optional) The number of signature operations Fireblocks expects back for this request. Not always sent. **asset** `string` The Fireblocks asset ID, e.g. `SOL_TEST`. A flattened copy of the same value inside `txMetaData`, kept in sync with it. **transactionType** `string` (Optional) The transaction's operation type, e.g. `TRANSFER`. A flattened copy of `txMetaData.transactionType`. Not always sent. **dstAddress** `string` (Optional) The primary destination address. A flattened copy of `txMetaData.dstAddress`. Not always sent. **dstTag** `string` (Optional) Destination tag or memo, for chains that support one. A flattened copy of `txMetaData.dstTag`. Not always sent. **amount** `number | string` The transaction amount. A number for transfers; the string `"0.0"` for [raw signing](#raw-signing-requests). **amountStr** `string` (Optional) The transaction amount as a string, for precision. Absent for raw signing. **amountInt** `string | number` (Optional) The amount in the asset's smallest unit (e.g. lamports for SOL). A string for transfers; the number `0` for raw signing. Not always sent. **amountUSD** `number` The USD value of the transfer. **fee** `number` The network fee, in the asset's smallest unit. **coinbaseFee** `string` (Optional) The network fee as a decimal string, in base asset units. An empty string for raw signing. Not always sent. **chaincode** `string` Hex-encoded HD wallet chaincode used to derive the signing key for this operation. **srcId** `string | number` The source ID. Can be a number or a string, so handle both types. **srcType** `string` The source type, e.g. `VAULT`. **destId** `string` The destination ID. **destType** `string` The destination type, e.g. `ONE_TIME`. Can also carry policy values such as `ONE_TIME_ADDRESS`, so it doesn't always match `txMetaData.dstType`. **tokenBlockchainId** (Optional) Sent for token transfers. **tokenSymbol** (Optional) The token's symbol. Sent for token transfers. **sequence** (Optional) Sent for XRP transactions where applicable. **changeIndex** (Optional) Sent for UTXO-based transactions where applicable. ### Raw signing requests For raw signing requests, the flat amount fields carry placeholder values: * `amount` is the string `"0.0"`. * `amountInt` is the number `0`. * `coinbaseFee` is an empty string (`""`). * `amountStr` is absent. The details of the messages to sign are in `txMetaData.extraParameters.rawMessageData`. See [Operation and chain-specific fields](#operation-and-chain-specific-fields). ### Signature verification Fireblocks Agent verifies the `policy_service` entry in `txMetaDataSignatures` against the Fireblocks signing certificate before it calls your `/messagesToSign` endpoint. If that signature doesn't validate, Fireblocks Agent discards the message and never forwards it to your server, so by the time your server receives a request, this check has already passed. Fireblocks Agent checks the `policy_service` entry; it doesn't verify `USER_AUTHORIZER` entries. If you want to verify the signature again on your side as an additional check, the algorithm is RSA with SHA-256 over the raw `txMetaData` string, and the signature is hex-encoded. ## `TxMetaData` The object you get from parsing `txMetaData`. It uses the same transaction and destination model as the [API Co-signer callback handler's transaction payload](/cosigners/callback-handler/approve-transactions), though field values aren't guaranteed to line up between the two (see the callouts below). ### Identifiers **txId** `string` The transaction's ID. **tenantId** `string` The workspace (tenant) ID. **transactionType** `string` The transaction's operation type, e.g. `TRANSFER`. **asset** `string` The Fireblocks asset ID. ### Initiator **userId** `string` The ID of the user or API user who created the transaction. **initiatorId** `string` Usually the same as `userId`. **userName** `string` (Optional) Display name of the initiating user. **userType** (Optional) The type of the initiating user. **impersonatedBy** (Optional) Initiator-related field, sent on some transactions. ### Source **srcType** `string` The source type, e.g. `VAULT`. **srcSubType** `string` (Optional) The specific source subtype. An empty string when not applicable. **srcId** `string` The source ID. Always a string here, unlike `metadata.srcId` (above), which can be a number or a string. **srcName** `string` (Optional) Display name of the source, e.g. the vault account's name. Can carry human-identifiable information, so handle it accordingly. **srcWalletId** `string` (Optional) Set for end-user-wallet sources. An empty string otherwise. **sourceTags** (Optional) Tags associated with the source. ### Destination The `dst*` fields in this section are conditional. They are omitted for typed message signing and for burns with no destinations. **HAS\_DESTINATIONS** `boolean` Whether this transaction uses the multi-destination [`destinations`](#destinations) array. Note the all-caps wire key, unusual among the otherwise camelCase fields here. **dstType** `string` (Conditional) The primary destination's type, e.g. `ONE_TIME_ADDRESS`. This value doesn't necessarily match the `dstType` enum documented for the [API Co-signer callback handler](/cosigners/callback-handler/approve-transactions#transactionrequestcallbackdestination) (`ONE_TIME_ADDRESS` here vs. `ONE_TIME` there, for what's otherwise the same kind of destination). **dstSubType** `string` (Optional) The specific destination subtype. An empty string when not applicable. **dstAddressType** `string` (Conditional) `ONE_TIME` or `WHITELISTED`. **dstId** `string` (Optional) The destination's ID. Empty for one-time addresses. **dstName** `string` (Optional) Display name of the destination, e.g. an exchange account or vault account name. Can carry human-identifiable information, so handle it accordingly. **dstAddress** `string` (Conditional) The primary destination's address. **displayDstAddress** `string` (Conditional) The display form of `dstAddress`. **dstTag** `string` (Optional) Destination tag or memo, for chains that support one. Not present for this Solana example. **displayDstTag** `string` (Optional) The display form of `dstTag`. **destinations** `array` Present for multi-destination transactions. One entry per destination. See [below](#destinations). ### Amounts and fees **amountNative** `number` The transfer amount. **amountNativeStr** `string` The transfer amount as a string, for precision. **amountUSD** `number` The USD value of the transfer. **amountUsdRate** `string` (Optional) The USD exchange rate used to compute `amountUSD`. Not always present, so don't treat it as required. **feeLevel** `string` The fee level used, e.g. `MEDIUM`. **feeAsset** `string` The asset the fee is paid in. **feeUsdRate** `string` (Optional) The USD exchange rate used for the fee asset. Not always present, so don't treat it as required. **isGrossAmount** `boolean` (Optional) Whether the fee is deducted from the requested amount. Not always present, so don't treat it as required. **enableCashOut** `boolean` (Optional) Whether cash-out is enabled for this transfer. Additional fee-related and nonce-related keys can appear, depending on the blockchain and the fee settings of the transaction. ### Blockchain details **blockchain** `string` The blockchain or network asset ID, e.g. `SOL_TEST`. **networkProtocol** `string` The network protocol, e.g. `SOL`. **testNet** `boolean` Whether this is a testnet transaction. **blockHash** `string` (Optional) Chain-specific. For Solana, the blockhash used to build the transaction. **nonceAccountAddress** `string` (Optional) Chain-specific. For Solana, the nonce account address. ### Policy verdict **action** `string` The policy verdict for the primary destination, e.g. `ALLOW`. **actionInfo** `object` Details about the policy rule that produced the verdict. Same shape as [`actionInfo` in the API Co-signer callback handler payload](/cosigners/callback-handler/approve-transactions#transactionrequestcallbackdestination): `ruleType`, `byGlobalPolicy`, `byRule`, `capturedRuleV2`, `capturedRuleNumV2`, `rulesSnapshotId`, and others. **authorizationGroups** `object` (Optional) Present when two-tier authorization applies. Same shape as [`authorizationGroups` in the API Co-signer callback handler payload](/cosigners/callback-handler/approve-transactions#transactionrequestcallbackdestination); the exact shape varies by policy version. Observed as `{"groups": []}` when not applicable. **policyVerdict** `string` The overall policy verdict for the transaction, e.g. `ALLOW`. For a single-destination transfer, the same as `action`. **signerId** `string` The Fireblocks API or Console user ID designated to sign, per the matched policy rule. **signerIds** `array` The full list of designated signer IDs. For a `SINGLE` designation, a single-element array matching `signerId`. ### Operation and chain-specific fields These fields appear only for specific operations or blockchains. Their inner structure isn't documented here; treat them as optional and parse them defensively. **extraParameters** `object` (Optional) Additional parameters. For raw signing, contains `rawMessageData`, which describes the messages to sign: `messages[]` (each with `content`), `derivationPath`, `algorithm`, and `preHash`. Can also contain internal instrumentation such as `sendToCoreStartTime` (a Unix timestamp in milliseconds). **contractCallData** (Conditional) Contract call data, for contract call transactions. **eip7702Authorizations** (Conditional) EIP-7702 authorizations, for EVM transactions that include them. **programCallDecoded** (Conditional) Decoded program call details, for Solana program calls. **sorobanCallData** (Conditional) Soroban smart contract call data, for Stellar. **nftTransfer** (Conditional) Present for NFT transfers. **travelRuleMessage** (Conditional) Present when the transaction carries a Travel Rule message. ### Other fields **multiDestFormat** `boolean` (Optional) Related to `HAS_DESTINATIONS`; whether the multi-destination format is in use. **enrichmentJson** `object` (Optional) Enrichment data. Shares its wire key with [`enrichmentJson` in the API Co-signer callback handler payload](/cosigners/callback-handler/approve-transactions#transactionrequestcallbackdestination), but the fields actually present vary by context. This is a loosely-typed bag, not a fixed schema. Observed here containing only `templateMessageName` (a string), which isn't part of the schema documented on that page. **txAdditionalDetails** `object` (Optional) Additional context about how the transfer was initiated. Same wire key as [`txAdditionalDetails` in the API Co-signer callback handler payload](/cosigners/callback-handler/approve-transactions#transactionrequestcallbackdestination), but again a loosely-typed bag. The fields observed here (`availableBalance`, `isAutoCalculatedGasLimit`, `useBroadcastingService`, `useBroadcastOrchestrator`, `psv2MultiInstanceFlow`) don't match the fields documented on that page. **transactionConfigurations** `object` (Optional) Reserved. Usually an empty object (`{}`). **requestContext** `object` (Optional) Reserved for Fireblocks use. Ignore it. **policyInspectionData** `object` (Optional) Reserved for Fireblocks use. Ignore it. ## `destinations` Present when `HAS_DESTINATIONS` is `true`. Each entry: **dstType** `string` Same value as the top-level `dstType` for a single-destination transfer. **dstSubType** `string` (Optional) An empty string when not applicable. **dstAddressType** `string` `ONE_TIME` or `WHITELISTED`. **dstAddress** `string` This destination's address. **dstId** `string` (Optional) This destination's ID. Empty for one-time addresses. **displayDstAddress** `string` The display form of `dstAddress`. **dstTag** `string` (Optional) This destination's tag or memo, for chains that support one. **displayDstTag** `string` (Optional) The display form of `dstTag`. **dstName** `string` (Optional) Display name of this destination. Can carry human-identifiable information, so handle it accordingly. **dstWalletId** (Optional) The destination wallet ID, where applicable. **reverseAddressLookup** (Optional) Address lookup data for this destination, where available. **networkTransferInfo** (Optional) Transfer network details, where applicable. **tags** (Optional) Tags associated with this destination. **amountNative** `number` The amount sent to this destination. **amountNativeStr** `string` Same amount, as a string. **amountUSD** `number` The USD value of the amount sent to this destination. **action** `string` The policy verdict for this specific destination. **actionInfo** `object` Same shape as the top-level `actionInfo` above, scoped to this destination. **txAdditionalDetails** `object` (Optional) Same wire key as the top-level field above, scoped to this destination. Usually an empty object (`{}`) here. **enrichmentJson** `object` (Optional) Same wire key as the top-level field above, scoped to this destination. ## Example An example `metadata` field for a Solana transfer (signatures and the payload to sign are shortened): ```json theme={"system"} { "signInfo": [{ "payload": "0201030766...", "path": [] }], "txMetaData": "{\"transactionType\":\"TRANSFER\",\"asset\":\"SOL_TEST\",\"userName\":\"Jane Doe\",\"srcType\":\"VAULT\",\"srcSubType\":\"\",\"srcId\":\"17\",\"srcName\":\"Test Vault Account\",\"srcWalletId\":\"\",\"txId\":\"50076a36-4580-4354-b8ea-b8c3e3e85479\",\"tenantId\":\"3827c4de-972e-5641-8a51-0a9f713aa708\",\"userId\":\"5d1adaa5-d62f-4628-9066-9f211a94c25b\",\"initiatorId\":\"5d1adaa5-d62f-4628-9066-9f211a94c25b\",\"HAS_DESTINATIONS\":false,\"enrichmentJson\":{\"templateMessageName\":\"TRANSFER_REQUEST\"},\"txAdditionalDetails\":{\"availableBalance\":\"3.899975879\",\"isAutoCalculatedGasLimit\":false,\"useBroadcastingService\":true,\"useBroadcastOrchestrator\":false,\"psv2MultiInstanceFlow\":true},\"dstType\":\"ONE_TIME_ADDRESS\",\"dstSubType\":\"\",\"dstId\":\"\",\"dstAddressType\":\"ONE_TIME\",\"dstAddress\":\"11111111111111111111111111111111111111111\",\"displayDstAddress\":\"11111111111111111111111111111111111111111\",\"amountUsdRate\":\"78.9920978006333\",\"amountNative\":1.0,\"feeLevel\":\"MEDIUM\",\"feeAsset\":\"SOL_TEST\",\"feeUsdRate\":\"78.9920978006333\",\"isGrossAmount\":false,\"enableCashOut\":false,\"blockHash\":\"22222222222222222222222222222222222222222\",\"nonceAccountAddress\":\"33333333333333333333333333333333333333333\",\"networkProtocol\":\"SOL\",\"blockchain\":\"SOL_TEST\",\"testNet\":true,\"transactionConfigurations\":{},\"amountNativeStr\":\"1\",\"amountUSD\":78.9920978,\"multiDestFormat\":false,\"action\":\"ALLOW\",\"actionInfo\":{\"byGlobalPolicy\":false,\"ruleType\":\"TENANT\",\"byRule\":true,\"capturedRuleNumV2\":\"1\",\"rulesSnapshotId\":159191},\"authorizationGroups\":{\"groups\":[]},\"signerId\":\"f6f44913-c8da-4e60-aa81-76ffd02633cc\",\"signerIds\":[\"f6f44913-c8da-4e60-aa81-76ffd02633cc\"],\"policyVerdict\":\"ALLOW\"}", "asset": "SOL_TEST", "dstAddress": "11111111111111111111111111111111111111111", "amountStr": "1.000000000", "amount": 1.0, "amountInt": "1000000000", "fee": 19000, "coinbaseFee": "0.000019000", "srcId": 17, "srcType": "VAULT", "destId": "", "destType": "ONE_TIME", "chaincode": "0000000000000000000000000000000000000000000000000000000000000000", "txMetaDataSignatures": [ { "id": "policy_service", "type": "SERVICE", "signature": "7845f537..." } ], "signingCount": 1, "amountUSD": 78.9920978 } ``` ## Changes to this page * **14 October 2026:** Corrected the types of `metadata.srcId`, `amount`, and `amountInt`. Documented raw signing values, additional `signInfo` properties, token and chain-specific flat fields, conditional `txMetaData` and `destinations` fields, and the top-level `timestamp` field. Marked flat fields that some paths omit as optional. Removed the statement that `version` is sent in signing payloads. Added the [Stability](#stability) section. # Launch Workflow Execution Source: https://developers.fireblocks.com/reference/launch-workflow-execution Once the WE is in **READY\_FOR\_LAUNCH** status, call the [POST /payments/workflow\_execution//actions/execute](/reference/launchflowexecution) endpoint. The WE proceeds through the following statuses. These statuses will be applied to each WEO and the WE as a whole. 1. **EXECUTION\_IN\_PROGRESS:** The execution process has started. 2. **EXECUTION\_COMPLETED** or **EXECUTION\_FAILED:** The execution completed or failed. These are finite states. # Legal Entity Setup Source: https://developers.fireblocks.com/reference/legal-entity-setup Register your LEI so counterparties see verified data for your addresses, and map vault accounts to legal entities through the Fireblocks API. A Legal Entity Identifier (LEI) is the 20-character GLEIF code that identifies a legal entity across jurisdictions. Registering one is optional if you only look up others in [Address Registry](/reference/address-registry), and required to return verified data and to use [About Address Registry Screening](/docs/address-registry-screening). If you don't have an LEI, Fireblocks can help you obtain one from a partner during setup in the Console. > [!NOTE] > > This legal entity is separate from the Travel Rule legal entity you create for [Sumsub](/docs/sumsub) or [GTR](/docs/gtr). Creating one does not create or change the other. ## What you can do through the API | Task | Console | API | | - | - | - | | Register a legal entity | Yes | [`POST /legal_entities`](/api-reference/compliance/register-a-new-legal-entity) | | List your legal entities | Yes | [List legal entities](/api-reference/compliance/list-legal-entities-paginated) | | Get one legal entity | Yes | [Get a legal entity](/api-reference/compliance/get-a-legal-entity) | | Update a legal entity | Yes | [Update legal entity](/api-reference/compliance/update-legal-entity) | | Delete a legal entity | Yes | [`DELETE /legal_entities/{legalEntityId}`](/api-reference/compliance/delete-a-legal-entity) | | List the vault accounts mapped to an entity | Yes | [List vault accounts for a legal entity](/api-reference/compliance/list-vault-accounts-for-a-legal-entity-paginated) | | Assign vault accounts to an entity | Yes | [Assign vault accounts to a legal entity](/api-reference/compliance/assign-vault-accounts-to-a-legal-entity) | ## Step 1: Register your legal entity Requires the Admin or Non-Signing Admin role. Accepts an optional `Idempotency-Key` header. | Field | Description | | - | - | | `lei` | Required. A valid 20-character LEI present in the GLEIF registry. | | `travelRuleProviders` | The Travel Rule providers to show counterparties, using short provider codes (for example `NOTABENE`, `MY_OWN`). | | `travelRuleContactEmail` | The contact email for Travel Rule communications. | ```json theme={"system"} { "id": "550e8400-e29b-41d4-a716-446655440000", "lei": "529900HNOAA1KXQJUQ27", "status": "PENDING", "isDefault": false, "travelRuleProviders": ["MY_OWN"], "travelRuleContactEmail": "compliance@example.com", "gleifData": { "lei": "529900HNOAA1KXQJUQ27", "legalName": "Example Corporation Ltd.", "legalAddressCountry": "US", "legalAddressRegion": "NY", "nextRenewalDate": "2025-12-31T00:00:00Z" } } ``` Fireblocks validates the LEI against GLEIF and reviews the registration. A workspace can register several legal entities. | Status | Meaning | | - | - | | `PENDING` | Under Fireblocks review. Your addresses appear as unverified in the meantime. | | `APPROVED` | Verified. Lookups against your addresses return your entity name, jurisdiction, and LEI. | | `DENIED` | Rejected. You can see the reason and resubmit. | | `REVOKED` | Previously approved, then revoked because the LEI expired or became invalid. | ## Step 2: Map vault accounts When Fireblocks approves your first LEI, every vault account maps to that entity by default. If you register more entities, assign specific vault accounts to them. An explicit assignment takes precedence over the default. ## Limitations * **Each vault account belongs to at most one legal entity.** * **Denied or revoked LEIs:** your addresses appear to counterparties as unverified, as before you registered, until you resubmit and Fireblocks approves it. * Address Registry limitations apply: not available on European or Swiss-hosted workspaces, or on Developer Sandbox workspaces. ## On the Help Center To register your legal entity in the Console, see [Legal Entity Setup](https://support.fireblocks.io/hc/en-us/articles/30614363410844-Legal-Entity-Setup). # Mint an NFT Source: https://developers.fireblocks.com/reference/mint-an-nft First, upload an image of your newly almost minted NFT. The minting function will receive it as one of its parameters. > [!NOTE] > > **Image hosting options** > > You can upload your selected image using any image-hosting site. > > While not covered in this guide, another option would be to use a system like [IPFS protocol](https://en.wikipedia.org/wiki/InterPlanetary_File_System) to store your image on-chain as a decentralized solution. Then we call upon the function while passing these values with it. | Parameter | Description | | - | - | | `sender` | The transaction signer address (your address) | | `tokenId` | The ID value of the newly minted NFT | | `uri` | The NFT's name, description, properties, and image location | Now we will use Hardhat for the actual deployment. First, we need to make sure it is well set up as described in the [Fireblocks Hardhat Plugin](/reference/hardhat-plugin) guide and then we follow the steps described in this guide. *** # Using Hardhat > [!NOTE] > > **Hardhat Runtime Environment required** > > We specifically require the Hardhat Runtime Environment. > > This is optional but useful for running the script in a standalone fashion through `node