{
 "slug": "fireblocks-bitgo-api-perimeter-gating-automated-custodial",
 "topic_id": "TOPIC-079",
 "cluster": "Digital Asset Custody",
 "tier": "Tier B",
 "title": "The custody API is the perimeter, and it authenticates a server",
 "summary": "Institutional custody platforms allow programmatic withdrawals within configured policy. The credential making those calls sits on a server, and a server can be compromised.",
 "lede": "Policy engines in custody platforms are genuinely good: thresholds, allow-lists, quorum requirements, velocity limits. Underneath all of it, the API call that initiates a withdrawal is authenticated by a key held on infrastructure.",
 "date": "2024-08-03",
 "category": "Crypto",
 "author_id": "whit-calloway",
 "tags": [
  "custody",
  "institutional",
  "API security",
  "withdrawals",
  "policy engine",
  "key management"
 ],
 "image_title": "Custody API Authenticates A Server",
 "schema": "Article",
 "key_takeaways": [
  "Policy engines constrain what a compromised API credential can do; they do not prevent it being used within policy.",
  "Automated withdrawal flows have no human at the moment of the transfer, which is the point of them and the exposure.",
  "Requiring a human signature over the rendered withdrawal for a defined tier keeps automation for routine flows and closes the top of the range."
 ],
 "body": [
  {
   "type": "h2",
   "text": "What the policy engine does"
  },
  {
   "type": "diagram",
   "kind": "flow",
   "alt": "A compromised server holding the API credential stays inside every configured policy bound",
   "caption": "The policy engine works exactly as designed. The attacker simply operates within it.",
   "nodes": [
    {
     "label": "Policy engine configured",
     "note": "allow-list, thresholds, velocity"
    },
    {
     "label": "API credential on a server",
     "note": "the perimeter",
     "bad": true
    },
    {
     "label": "Server compromised",
     "note": "no policy violated",
     "bad": true
    },
    {
     "label": "Sub-threshold withdrawals, repeatedly",
     "note": "to an allow-listed address",
     "bad": true
    },
    {
     "label": "Velocity cap reached",
     "note": "the real limit",
     "bad": true
    }
   ]
  },
  {
   "type": "p",
   "html": "Institutional platforms let an organisation express rules about what can move, to where, by whom, and how fast."
  },
  {
   "type": "table",
   "head": [
    "Control",
    "Effect"
   ],
   "rows": [
    [
     "Destination allow-list",
     "Withdrawals only to pre-approved addresses"
    ],
    [
     "Threshold tiers",
     "Larger amounts require more approvers"
    ],
    [
     "Velocity limits",
     "Caps per period regardless of individual transaction size"
    ],
    [
     "Quorum requirements",
     "Multiple approvers for defined categories"
    ],
    [
     "Time-of-day restrictions",
     "Transactions outside a window are blocked or delayed"
    ]
   ]
  },
  {
   "type": "p",
   "html": "These are well designed and genuinely reduce exposure. An attacker with the API credential is bounded by them, which is the point."
  },
  {
   "type": "h2",
   "text": "What remains inside the boundary"
  },
  {
   "type": "p",
   "html": "Everything the policy permits. An attacker who has compromised the server holding the API credential can do whatever an automated flow is allowed to do."
  },
  {
   "type": "ul",
   "items": [
    "Withdrawals to any address on the allow-list — and allow-lists are often longer than anyone reviews",
    "Amounts below the threshold requiring human approval, repeatedly, up to the velocity limit",
    "Any operation the automation identity is permitted to perform"
   ]
  },
  {
   "type": "p",
   "html": "The second point is where the loss accumulates. An attacker patient enough to stay under the threshold extracts value continuously without triggering anything."
  },
  {
   "type": "h2",
   "text": "The allow-list question"
  },
  {
   "type": "p",
   "html": "Worth examining separately because it is the control most organisations rely on most heavily."
  },
  {
   "type": "p",
   "html": "An allow-list constrains destinations. Its strength therefore depends entirely on how addresses get onto it and how often it is pruned. In practice: addresses accumulate, the process for adding is lighter than the process for withdrawing, and nobody removes stale entries."
  },
  {
   "type": "p",
   "html": "This suggests the highest-value gate is not the withdrawal at all — it is the allow-list addition, which is a rarer event with larger consequences."
  },
  {
   "type": "h2",
   "text": "Tiering with a human signature"
  },
  {
   "type": "code",
   "text": "# Withdrawal request arrives at the custody API wrapper\n\ntier = classify(request)     # amount, destination age, asset\n\nif tier == ROUTINE:\n    # Below threshold, established destination, normal velocity\n    forward_to_custody_api(request)\n\nelse:\n    # Above threshold, new destination, or unusual pattern\n    statement = render(request)        # full destination, amount,\n                                       # asset, destination age,\n                                       # cumulative period total\n    receipt = await_human_signature(statement)\n    verify(receipt) or refuse()\n    forward_to_custody_api(request, receipt=receipt)"
  },
  {
   "type": "p",
   "html": "The rendered statement should include the cumulative total for the period. An individual withdrawal below the threshold looks routine; the fourteenth one that day does not, and only the cumulative figure reveals it."
  },
  {
   "type": "h2",
   "text": "Where the gate sits"
  },
  {
   "type": "p",
   "html": "An important design choice. A wrapper in front of the custody API can be bypassed by anything that can call the API directly."
  },
  {
   "type": "table",
   "head": [
    "Placement",
    "Robustness"
   ],
   "rows": [
    [
     "Wrapper service in your infrastructure",
     "Bypassed if the attacker calls the custody API directly"
    ],
    [
     "Enforced by custody platform policy",
     "Robust — requires the platform to support receipt verification"
    ],
    [
     "Both",
     "Wrapper for rendering and workflow; platform policy as the enforcement"
    ]
   ]
  },
  {
   "type": "p",
   "html": "The third row is the realistic architecture today, and it depends on the platform's policy engine supporting an external approval requirement. Where it does not, a wrapper plus tight network and credential restrictions is the available approximation."
  },
  {
   "type": "h2",
   "text": "What to ask a custody provider"
  },
  {
   "type": "ol",
   "items": [
    "Can a withdrawal require an approval your platform cannot itself generate — an externally verified signature?",
    "What exactly does your API credential authenticate, and what would an attacker holding it be able to do within our policy?",
    "Can allow-list additions be gated separately and more strictly than withdrawals?",
    "Do your policy decisions produce evidence we can verify without calling you?",
    "What is the maximum value extractable in twenty-four hours under our current policy, without any human approval?"
   ]
  },
  {
   "type": "p",
   "html": "The fifth question is the one to ask first. Most organisations have never computed it, and the number is usually larger than the treasurer expects."
  },
  {
   "type": "h2",
   "text": "The allow-list is the weak point, not the threshold"
  },
  {
   "type": "p",
   "html": "An allow-list constrains destinations, so its strength depends entirely on how addresses get onto it and how often it is pruned. In practice addresses accumulate, the process for adding is lighter than the process for withdrawing, and nobody removes stale entries. That suggests the highest-value gate is not the withdrawal at all — it is the allow-list addition, a rarer event with larger consequences."
  },
  {
   "type": "table",
   "caption": "Where the gate should sit",
   "head": [
    "Placement",
    "Robustness"
   ],
   "rows": [
    [
     "Wrapper in your own infrastructure",
     "Bypassed by anything that can call the custody API directly"
    ],
    [
     "Enforced in the platform's policy engine",
     "Robust — requires the platform to support external approval"
    ],
    [
     "Both",
     "Realistic today: wrapper for rendering and workflow, platform policy for enforcement"
    ]
   ]
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“Our velocity limits cap the loss.”</strong> They cap it at the velocity limit, which is the number worth computing. Ask what the maximum extractable value is in twenty-four hours under current policy with no human approval. Most treasurers have never computed it, and it is larger than expected."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“We require quorum above a threshold.”</strong> Then the attack runs below the threshold. Render the cumulative period total in the approval, not just the individual amount — the fourteenth sub-threshold withdrawal that day is the signal, and only the cumulative figure shows it."
  }
 ],
 "faq": [
  {
   "q": "Are custody policy engines inadequate?",
   "a": "No. They meaningfully bound what a compromised credential can do. They do not prevent it being used within policy, which is where patient attackers operate."
  },
  {
   "q": "Why is the allow-list the weak point?",
   "a": "Addresses accumulate, the addition process is lighter than the withdrawal process, and stale entries are rarely pruned. The addition is the rarer, higher-consequence event."
  },
  {
   "q": "Why show cumulative totals in the approval?",
   "a": "An individual sub-threshold withdrawal looks routine. The fourteenth that day does not, and only the cumulative figure reveals the pattern."
  },
  {
   "q": "Where should the gate be enforced?",
   "a": "In the custody platform's policy engine where supported. A wrapper in your own infrastructure can be bypassed by direct API calls."
  },
  {
   "q": "Why gate the allow-list rather than the withdrawal?",
   "a": "Because addresses accumulate through a lighter process than withdrawals use, and a hostile entry converts every future policy check into a pass."
  },
  {
   "q": "Why show cumulative totals at approval?",
   "a": "A single sub-threshold withdrawal looks routine. The pattern only becomes visible in the period total."
  }
 ],
 "sources": [
  {
   "t": "FTC — business guidance on marketplaces and consumer protection",
   "u": "https://www.ftc.gov/business-guidance"
  },
  {
   "t": "NYDFS 23 NYCRR Part 500 — Cybersecurity Requirements",
   "u": "https://www.dfs.ny.gov/industry_guidance/cybersecurity"
  },
  {
   "t": "NIST SP 800-57 Part 1 Rev. 5 — Key Management",
   "u": "https://csrc.nist.gov/pubs/sp/800/57/pt1/r5/final"
  },
  {
   "t": "NYDFS — virtual currency business guidance",
   "u": "https://www.dfs.ny.gov/virtual_currency_businesses"
  }
 ],
 "related": [
  {
   "slug": "multi-party-computation-mpc-vs-seen-signed-receipts",
   "title": "MPC versus seen-and-signed",
   "category": "Comparison"
  },
  {
   "slug": "nydfs-virtual-currency-guidance-meeting-cybersecurity-fraud-escalation",
   "title": "Meeting supervisory audit expectations",
   "category": "Crypto"
  },
  {
   "slug": "bybit-wazirx-multisig-hack-payload-mismatch-postmortem",
   "title": "The payload flip",
   "category": "Crypto"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/Custody%20API%20Authenticates%20A%20Server/1200_630/blog.jpg",
 "wordcount": 971,
 "url": "/blog/fireblocks-bitgo-api-perimeter-gating-automated-custodial.html",
 "reading_time": "4 min read",
 "seo_title": "The custody API authenticates a server, not a human",
 "meta_description": "Institutional custody platforms allow programmatic withdrawals within policy. The credential making those calls sits on a server.",
 "hub": {
  "slug": "topics/digital-asset-custody",
  "title": "Digital asset custody"
 },
 "answer": "Everything the policy permits. Platforms such as Fireblocks and BitGo bound withdrawals with allow-lists, thresholds, velocity limits and quorum rules. A credential on a compromised server operates inside all of them — repeatedly, under the threshold, to any address already on the list.",
 "answer_q": "What can an attacker do with a stolen custody API credential?",
 "entities": [
  {
   "name": "Fireblocks",
   "type": "Organization",
   "url": "https://www.fireblocks.com/",
   "primary": true
  },
  {
   "name": "BitGo",
   "type": "Organization",
   "url": "https://www.bitgo.com/"
  }
 ],
 "glossary": [
  {
   "term": "Policy engine",
   "def": "Rules constraining what a custody credential may do: destination allow-lists, value thresholds, velocity caps, quorum requirements and time windows."
  },
  {
   "term": "Allow-list",
   "def": "The set of approved withdrawal destinations. Effective in proportion to how carefully entries are added and how often they are pruned."
  },
  {
   "term": "Velocity limit",
   "def": "A cap on value moved per period, independent of individual transaction size. Frequently the real bound on a compromise."
  }
 ],
 "checklist": {
  "title": "Five questions for your custody provider",
  "id": "ask",
  "desc": "The fifth is the one to ask first.",
  "steps": [
   {
    "name": "Can a withdrawal require an approval your platform cannot itself generate?",
    "text": "An externally verified signature, not an internal workflow state."
   },
   {
    "name": "What exactly does the API credential authenticate?",
    "text": "And what could a holder do inside our current policy?"
   },
   {
    "name": "Can allow-list additions be gated more strictly than withdrawals?",
    "text": "The addition is the higher-consequence event."
   },
   {
    "name": "Do policy decisions produce evidence we can verify without calling you?",
    "text": "Otherwise the record ends when the relationship does."
   },
   {
    "name": "What is the maximum extractable in 24 hours with no human approval?",
    "text": "Compute it. This number decides how much of the above matters."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "Manav sits in front of the custody API as a tier gate: routine withdrawals pass, and anything above threshold, to a new destination, or with an unusual cumulative total requires a signature over the rendered transfer that the platform itself cannot produce.",
  "href": "../docs.html",
  "label": "See withdrawal gating"
 }
}