{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-developers/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition","img"]},"redocly_category":"Developers","type":"markdown"},"seo":{"title":"Build a Custom API Approver","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"build-a-custom-api-approver","__idx":0},"children":["Build a Custom API Approver"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use an API User and webhooks to build a custom API Approver that performs"," ","automated validation checks on Fordefi transactions."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An API Approver extends Fordefi's built-in policy engine with your own custom"," ","business logic. While Fordefi policies cover many common and advanced approval"," ","conditions, you may have organization-specific requirements that aren't"," ","natively supported. An API Approver lets you enforce those custom rules"," ","programmatically before a transaction is approved."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The API Approver is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["chain agnostic"]}," and can inspect transaction payloads on"," ","any supported blockchain. For example, you can use it to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Inspect the parameters of a Solana program instruction to verify that it"," ","invokes an approved program with the expected accounts and arguments."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Parse and validate any field in EVM EIP-712 messages."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Verify that the recipient of a bridge transaction is on an approved"," ","whitelist."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Restrict transactions to approved methods on a Solana program."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Validate transaction data against internal systems or compliance databases."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Enforce custom business rules that are unique to your organization."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Note"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An API Approver does ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," cryptographically sign transactions."," ","It evaluates the transaction payload against your custom validation logic and"," ","either approves the transaction or aborts it using an API User token."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"before-you-start","__idx":1},"children":["Before you start"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To run this setup you will need to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create an API User with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Trader"]}," role and save its access tokens."," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.fordefi.com/developers/getting-started/create-an-api-user"},"children":["Learn more"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The example that follows, below, uses an API User named ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Validator Bot"]},", which will serve as our"," ","custom API Approver and enforce the validation rules you define."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set up a modified webhook server that will receive events from Fordefi."," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.fordefi.com/developers/webhooks"},"children":["Learn more"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The instructions below use a"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/FordefiHQ/api-examples/tree/main/python/cosigner"},"children":["modified server"]}," ","designed to enforce a number of custom validation rules. For example, it"," ","can inspect the parameters of a Solana program instruction, validate"," ","calldata or EIP-712 messages in EVM transactions, verify that a bridge"," ","destination is whitelisted, or implement any other organization-specific"," ","approval logic."]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"secure-your-api-approver-server","__idx":2},"children":["Secure your API Approver server"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Since the API Approver has the authority to approve or abort transactions, it's"," ","critical to lock down both the webhook endpoint it exposes and the API User"," ","credentials it uses."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Verify webhook signatures."]}," Every webhook that Fordefi sends includes a"," ","signature header. You should verify this signature against Fordefi's public key"," ","to confirm that the payload genuinely originated from Fordefi and that it"," ","hasn't been tampered with. See the"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.fordefi.com/developers/webhooks#validate-a-webhook"},"children":["webhook validation documentation"]}," ","for the public key and implementation details."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Whitelist Fordefi's IP."]}," Restrict inbound traffic to your API Approver server"," ","by whitelisting Fordefi's NAT address:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"54.243.103.88\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This ensures that only Fordefi's infrastructure can reach your webhook"," ","endpoint. Any requests originating from other IPs can be dropped at the"," ","firewall or load balancer level before they ever hit your application."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restrict the API User to trusted outbound IPs."]}," Configure the API User used"," ","by your API Approver to only accept API requests originating from your"," ","Approver's static outbound IP address(es). This limits the impact of a leaked"," ","API token by preventing it from being used outside your trusted infrastructure."," ","See"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.fordefi.com/developers/getting-started/create-an-api-user#edit-whitelisted-ips-for-an-api-user"},"children":["Edit whitelisted IPs for an API User"]}," ","for configuration instructions."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Combining webhook signature verification, inbound IP whitelisting and API User"," ","IP restrictions provides defense in depth against spoofed webhook requests and"," ","unauthorized use of your API credentials."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"workflow","__idx":3},"children":["Workflow"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create a policy with a single-set approval quorum including ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Validator Bot"]}," ","as your custom API Approver:"]},{"$$mdtype":"Tag","name":"Image","attributes":{"src":"/assets/approve-tx.1cf160b13eced5fd68cb317186eafbb0968fc95a2bd1786a6ea6d87c16dd8154.9c1bb791.png","alt":"alt","framed":false,"withLightbox":false,"width":"560","border":"1px solid lightgray"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This quorum implements the following logic: The API Approver"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["only"]}," approves transactions that pass your custom validation checks. If validation"," ","fails, it automatically aborts the transaction using the"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/transactions/{tx_id}/abort"]}," endpoint documented"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.fordefi.com/api/openapi/transactions/abort_transaction_api_v1_transactions__id__abort_post"},"children":["here"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," You can add additional approval sets, including human approvers, to"," ","the quorum. In this example, however, the API Approver is designed to keep"," ","the approval flow as automated as possible."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Build the rest of the policy:"]},{"$$mdtype":"Tag","name":"Image","attributes":{"src":"/assets/approve-tx-policy.da0ff169b7fe32d706a797b8ca08a8863831102154fb7369a2f9364c0a2eb773.9c1bb791.png","alt":"alt","framed":false,"withLightbox":false,"width":"700","border":"1px solid lightgray"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Configure the policy so it matches every transaction that should be evaluated"," ","by your API Approver. Depending on your use case, this may include all"," ","vaults, specific vault groups, particular transaction types, or other policy"," ","criteria."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Important"},"children":[{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The policy should be ranked above more specific rules so that every"," ","relevant transaction is evaluated before approval."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["In the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Initiator"]}," area: Make sure you do not select ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Any"]},", so as not to include ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Validator Bot"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["In the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Origin"]}," area: Choose the group of vaults or vault groups you wish to monitor."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Fordefi's backend assumes that the transaction initiator always provides"," ","tacit approval, so it's very important that the API Approver API User is"," ","never used to initiate transactions. This is why the example does not use"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Any"]}," as the initiator in the policy."]}]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create transactions that match your policy and monitor your webhook server"," ","and policy matching. If all works as expected, any transaction that passes"," ","your validation rules will be approved automatically. Transactions that fail"," ","validation will be automatically aborted by the API Approver before they can"," ","proceed to cryptographic signing by the configured signer(s)."]}]}]}]},"headings":[{"value":"Build a Custom API Approver","id":"build-a-custom-api-approver","depth":1},{"value":"Before you start","id":"before-you-start","depth":2},{"value":"Secure your API Approver server","id":"secure-your-api-approver-server","depth":2},{"value":"Workflow","id":"workflow","depth":2}],"frontmatter":{"seo":{"title":"Build a Custom API Approver"}},"lastModified":"2026-08-05T07:55:13.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/developers/transaction-types/build-custom-api-approver","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}