<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Pool API Field Notes</title>
<link>https://poolapi.com/blog/</link>
<description>Architecture guides for pooled credits, models, prompts, compute, assets, nodes, endpoints, governance, and API marketplaces.</description>
<language>en-us</language>
<lastBuildDate>Tue, 20 Oct 2026 00:00:00 +0000</lastBuildDate>
<generator>PoolAPI.com static site</generator>
<image><url>https://poolapi.com/assets/images/poolapi-logo.png</url><title>Pool API Field Notes</title><link>https://poolapi.com/blog/</link></image>
<item>
<title>API Marketplace and Portal Design for Shared Models, Compute, Assets, and Data</title>
<link>https://poolapi.com/blog/api-marketplace-portal-design/</link>
<guid isPermaLink="true">https://poolapi.com/blog/api-marketplace-portal-design/</guid>
<pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate>
<category>API Operations</category>
<description><![CDATA[A complete blueprint for catalog metadata, onboarding, documentation, access plans, metering, lifecycle, trust, and machine-readable discovery.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">An API marketplace or portal makes services discoverable, understandable, and governable. In a Pool API context, the catalog may include model routes, prompt libraries, credit products, compute shapes, node capacity, token operations, databases, CMS connectors, domain inventory, webhooks, and agent tools. A useful portal is more than a grid of logos. It connects a human-readable product description to a machine-readable contract, access policy, lifecycle, usage model, and support path.</p>
<p>The marketplace and gateway should remain conceptually separate. The marketplace describes and governs offerings. The gateway authenticates and routes runtime traffic. Combining everything into one application makes catalog changes risky and encourages product metadata to leak into hot-path routing. A portal can publish immutable specifications and plan references while the gateway enforces the current resolved policy.</p>
<h2 id="catalog">Create a normalized catalog entry</h2><h3 id="catalog-detail">Normalize without erasing differences</h3>
<p>Each offering needs a stable identifier, name, provider, summary, category, capabilities, audience, regions, lifecycle state, documentation, specification references, authentication methods, plan options, support contact, terms, and change history. Preserve provider-specific extensions rather than flattening every service into the same fields. A model route and a database API share catalog basics but have different capability details.</p>
<p>Use namespaces so two providers can publish similarly named products without collision. Separate the logical product from versions and deployment instances. A product may have several API versions and several regional endpoints. A plan may grant access to only part of the product. Keeping these entities distinct makes deprecation, pricing, and routing manageable.</p>
<h2 id="specifications">Publish machine-readable contracts</h2>
<p>The OpenAPI Specification defines a language-agnostic description for HTTP APIs; the authoritative <a href="https://spec.openapis.org/oas/v3.2.0.html" rel="noopener">OpenAPI 3.2 specification</a> is a strong foundation for endpoint, parameter, schema, and security documentation. Event-driven products can publish an AsyncAPI document. GraphQL, gRPC, and tool protocols need their corresponding schemas. The catalog should identify the format and exact version rather than offering one generic “API spec” link.</p>
<p>Validate specifications on ingestion. Resolve references, reject dangerous external fetches, and scan for secrets. Generate examples and documentation from the contract, but let owners add conceptual guides that explain workflows and policy. A complete schema does not tell a developer when to reserve credits, how to handle a webhook replay, or which model profile fits a task.</p>
<h2 id="onboarding">Design provider onboarding as a workflow</h2>
<p>A provider submits ownership, product metadata, specification, endpoint verification, support information, security contacts, and plan definitions. Automated checks validate URLs, schemas, authentication declarations, example requests, and health. Reviewers approve the listing before publication. High-risk categories such as financial movement or untrusted compute may require additional security and legal review.</p>
<p>Keep draft, review, approved, published, suspended, deprecated, and retired states. Publishing should reference an immutable catalog revision. A provider can prepare the next revision without changing the public listing. Emergency suspension blocks new access and can instruct the gateway to stop routing while preserving the catalog history and customer notices.</p>
<h2 id="consumer-access">Separate discovery from access</h2>
<p>Public discovery can show what an API does without granting credentials. Access requests create a relationship between a consumer account, application, product version, plan, and policy. Some products can approve automatically; others require provider review, identity verification, contract acceptance, or a technical questionnaire. Record the approval decision and terms version.</p>
<p>Issue credentials through the gateway or dedicated identity system, not inside static documentation. Use scopes tied to product capabilities. Let consumers rotate keys, view last use, and revoke applications. For agent clients, prefer short-lived tokens or delegated credentials over embedding a permanent marketplace key in prompts or code bundles.</p>
<h2 id="plans">Model plans, quotas, and credits explicitly</h2><h3 id="plans-detail">Separate access tiers from metered usage</h3>
<p>A plan defines access scope, quota dimensions, concurrency, rate limits, support, region, and billing or credit rules. Avoid hard-coding plan names into the gateway. The access grant references a plan version resolved into enforceable policy. If a plan changes, existing grants can remain on the old version or migrate through an explicit process.</p>
<p>Keep request limits separate from credit balances. A free plan may have a daily quota without prepaid credits. A paid plan may have a balance plus per-minute limits. A compute marketplace may reserve GPU-seconds, while a model marketplace meters input and output units. The catalog can display normalized examples but must preserve the underlying unit and conversion version.</p>
<h2 id="metering">Collect metering at the enforcement point</h2>
<p>Usage records should originate as close as possible to the gateway or resource controller that observed the work. Each record includes consumer, product, version, endpoint or operation, provider, unit quantities, timestamp, trace, policy version, and idempotency identifier. Send records to an immutable ledger or event stream, then aggregate for dashboards and invoices.</p>
<p>Do not trust provider-submitted totals as the only source when the marketplace controls routing. Reconcile gateway observations with provider reports. For long-running compute, meter leases and measured execution. For asynchronous jobs, separate submission from completed work. The consumer should be able to inspect usage records and dispute specific identifiers.</p>
<h2 id="trust">Make trust evidence specific</h2>
<p>Badges such as “verified” are ambiguous unless the portal states what was verified. Ownership verification, specification validation, security review, uptime history, identity review, and financial licensing are different claims. Publish the scope, reviewer, date, and expiry where appropriate. Do not invent customer ratings, partner logos, or certification status.</p>
<p>Operational signals can include recent availability, latency class, incident status, and support response target, but label them as measured by the marketplace or declared by the provider. Protect against gaming and low sample sizes. A marketplace can rank relevance without implying quality, or let consumers filter by capabilities and policy rather than presenting one opaque score.</p>
<h2 id="lifecycle">Treat deprecation as a product workflow</h2>
<p>Every version needs a lifecycle and dates. Deprecation announces that a version remains available but should not receive new integrations. Sunset states when traffic will stop. Retirement blocks use while preserving documentation and history. Send notices to affected access grants and expose machine-readable deprecation fields in the catalog and gateway responses.</p>
<p>A breaking change should create a new version. Nonbreaking documentation improvements can update the catalog revision. Provide migration guides and, where possible, compatibility reports generated from specifications. Do not silently point a versioned URL at incompatible behavior. Agents and generated clients need the same stability as human developers.</p>
<h2 id="search">Support search by capability and constraints</h2>
<p>Keyword search is useful, but Pool API discovery often needs structured filters: resource type, modality, authentication, region, latency class, input format, output contract, network, compute shape, provider, lifecycle, and plan. Use controlled vocabularies with extensible namespaces. Allow a product to declare several categories without duplicating the listing.</p>
<p>For machine clients, provide a catalog API that returns compact capability records and links to full specifications. Apply authorization before revealing private products or negotiated plans. Agents should not receive every marketplace secret or endpoint; they receive the subset allowed for the workflow and a short-lived route or tool reference.</p>
<h2 id="portal-ux">Write documentation around tasks</h2>
<p>Organize each product page around what the consumer can accomplish, prerequisites, authentication, first successful request, error handling, limits, webhooks, versioning, and support. Include tested examples in common languages, but keep the canonical request and response schema visible. Show example data, never live credentials or customer information.</p>
<p>A “try it” console is a data-entry tool and requires security, consent, rate limits, and credential handling. A static editorial portal can instead provide copyable examples and link to a secure external console when one actually exists. Do not add decorative buttons that imply account creation, key issuance, or purchases when those functions are unavailable.</p>
<h2 id="api-shape">A catalog and access surface</h2>
<pre><code>GET  /catalog/products
GET  /catalog/products/{id}
GET  /catalog/products/{id}/versions
GET  /catalog/products/{id}/specifications
POST /provider-submissions
POST /access-requests
GET  /access-grants
GET  /usage-records
GET  /change-notices</code></pre>
<p>The public catalog can be cacheable. Access and usage endpoints are tenant-scoped and private. Return links to canonical documentation, terms, status, and support. Use content negotiation or explicit download links for specifications rather than embedding a huge schema in every product response.</p>
<h2 id="governance">Govern the marketplace itself</h2>
<p>Define who can create providers, approve products, publish revisions, suspend traffic, change plans, view usage, and resolve disputes. Separate reviewer and provider roles. High-impact changes should require expected versions and, where appropriate, multiple approvals. Every catalog and policy mutation gets an audit event.</p>
<p>Moderate listings for misleading claims, copied documentation, hidden fees, unsafe endpoints, and unsupported compatibility promises. Establish an appeals path. Marketplace governance should be published in understandable terms, especially when rankings or suspension can materially affect providers.</p>
<h2 id="conclusion">A portal is a contract directory</h2>
<p>A strong API marketplace connects discovery, machine-readable contracts, access policy, metering, lifecycle, and trust evidence. Keep the catalog separate from runtime routing, validate specifications, version plans, and publish precise deprecation dates. Make every trust claim specific and every interactive function real.</p>
<p>Explore the static <a href="/api-portal/">Pool API Portal blueprint</a>, the <a href="/marketplace/">Marketplace architecture page</a>, and the complete set of <a href="/blog/">Pool API Field Notes</a>.</p>
]]></content:encoded>
</item><item>
<title>Governance Pool API for Votes, Points, Awards, and Donation Allocations</title>
<link>https://poolapi.com/blog/governance-pool-api-votes-points-donations/</link>
<guid isPermaLink="true">https://poolapi.com/blog/governance-pool-api-votes-points-donations/</guid>
<pubDate>Thu, 16 Jul 2026 00:00:00 +0000</pubDate>
<category>Pool API Fundamentals</category>
<description><![CDATA[A careful design for eligibility, snapshots, ballots, point ledgers, awards, donation budgets, approvals, privacy, and audit trails.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A governance pool API coordinates scarce decision rights or allocatable value across voters, members, judges, donors, projects, or automated agents. The pool may contain votes, reputation points, award credits, matching funds, donation budgets, or proposal capacity. These units look similar in a dashboard, but they carry different meanings. A vote expresses a decision. A point may measure participation. An award may be discretionary recognition. A donation allocation may move real money.</p>
<p>The first design rule is to keep these ledgers separate. Combining them into one “score” creates ambiguous rights and makes manipulation difficult to detect. The second rule is to snapshot eligibility and rules before a decision opens. The third is to preserve an audit trail without exposing private ballots or sensitive donor data.</p>
<h2 id="unit">Define the governance unit and its authority</h2><h3 id="unit-detail">Separate influence from value</h3>
<p>For each pool, state what the unit can do, who issues it, whether it expires, whether it can be delegated, and whether it is transferable. One person may have one ballot but many nonvoting contribution points. Award credits may be issued to judges and spent on nominees. Donation credits may represent an instruction to distribute a sponsor’s budget, not a withdrawable balance.</p>
<p>Use distinct unit identifiers and account types. Never let a client send a free-form unit name and assume the server understands its legal or governance effect. The API should resolve a policy version that defines issuance, eligibility, weighting, caps, and settlement. Display labels can change; the underlying unit and rule identifiers should remain stable.</p>
<h2 id="identity">Choose an identity and eligibility model</h2>
<p>Eligibility can be based on membership, verified identity, account age, contribution, token ownership, geography, employment, donor status, or an external registry. Each basis has privacy and fairness implications. The API should store the evidence reference and decision, not more personal data than necessary. Some programs need one-person-one-vote assurance; others intentionally weight organizations or contributions.</p>
<p>NIST’s current <a href="https://csrc.nist.gov/pubs/sp/800/63/4/final" rel="noopener">Digital Identity Guidelines</a> describe identity proofing, authentication, and federation concepts that can inform assurance decisions, though a community or private program should tailor controls to its own risk rather than claiming federal compliance. Record the assurance method and date used for eligibility, and allow appeals or corrections through a governed process.</p>
<h2 id="snapshot">Snapshot the electorate and rules</h2><h3 id="snapshot-detail">Freeze eligibility reproducibly</h3>
<p>When a ballot or allocation round opens, create an immutable snapshot of eligible accounts, weights, proposal set, unit balances, start and end times, tally method, quorum, tie rule, and disclosure policy. Without a snapshot, membership or point changes during the round can alter outcomes unpredictably. Later corrections should be explicit amendments with authority and a public or auditable reason.</p>
<p>Use a snapshot identifier in every ballot. A voter request should not depend on the current profile alone. If the account was eligible in the snapshot and later changes status, the policy defines whether the ballot remains valid. If a proposal is withdrawn, preserve its identifier and state rather than deleting it from history.</p>
<h2 id="ballot">Separate ballot issuance from casting</h2>
<p>Ballot issuance confirms that an eligible account has the right units for a round. Casting consumes or locks those rights according to the method. In a simple election, a ballot may be replaceable until close, with only the final version counted. In cumulative voting, units can be distributed across choices. In ranked choice, the ballot stores an ordered list rather than subtracting a balance per candidate.</p>
<p>Make idempotency and replacement rules explicit. Retrying the same cast request should not create another vote. A new request with an expected ballot version can replace the prior ballot when policy allows. The server returns a receipt identifier and status without necessarily revealing the private selection in ordinary logs.</p>
<h2 id="privacy">Design ballot privacy deliberately</h2>
<p>Some decisions are public and attributable. Others require secret ballots. Do not promise anonymity if administrators can trivially join voter identity and ballot rows. Separate eligibility records from encrypted ballot content, restrict join capability, and consider cryptographic or third-party techniques when strong secrecy is required. Explain what metadata remains visible, such as participation time or round membership.</p>
<p>Audit does not require publishing private selections. The system can prove that eligible ballots were included, duplicates were excluded, and the tally used the declared rules through controlled evidence and independent review. Retention should reflect the program’s obligations. Delete unnecessary authentication events while preserving the minimum record needed to verify the outcome.</p>
<h2 id="points">Keep point issuance append-only</h2>
<p>Points and awards need an immutable ledger with grant, earn, spend, expire, revoke, and adjustment entries. Each entry names the issuing authority, reason, evidence reference, policy version, and idempotency key. Corrections are compensating entries. A public leaderboard may use an aggregate, but the underlying ledger remains the source of truth.</p>
<p>Prevent circular issuance. A user should not create an activity that grants points and approve the same grant unless the program explicitly permits self-attestation. Apply caps, review thresholds, and anomaly detection. Separate reputation that affects governance from redeemable rewards; otherwise purchasing or trading a reward can silently purchase influence.</p>
<h2 id="donations">Model donation allocations as budget instructions</h2>
<p>A donation allocation pool may give participants credits to direct a sponsor’s budget among approved recipients. The credits are not necessarily cash held for the participant. The API should distinguish pledged budget, allocated amount, approved distribution, payment submitted, and payment settled. Recipient eligibility and payment details belong in a controlled registry.</p>
<p>Apply caps, matching formulas, geographic or cause restrictions, and conflict-of-interest rules through versioned policy. A matching round may need anti-sybil controls because splitting one contribution across many identities can distort the match. Preserve raw contribution and allocation evidence for review, while limiting public disclosure of personal donor information.</p>
<h2 id="approvals">Use multi-step approvals for consequential actions</h2>
<p>Publishing a tally, issuing a large award, changing eligibility, or releasing donation funds may require more than one role. Represent approval requests with required approver classes, threshold, deadline, and conflict rules. An approver signs a decision against an immutable payload hash. If the payload changes, prior approvals no longer apply.</p>
<p>Emergency actions such as pausing a round should be narrow, time-bounded, and auditable. A pause stops new ballots without editing existing ones. Cancellation preserves the round and reason. Reopening after close should require a new policy decision rather than a hidden timestamp edit.</p>
<h2 id="api-shape">A governance pool surface</h2>
<pre><code>GET  /governance-pools
POST /rounds
POST /rounds/{id}/snapshots
GET  /rounds/{id}/eligibility/{account_id}
POST /ballots
PUT  /ballots/{id}
POST /rounds/{id}/close
POST /rounds/{id}/tallies
GET  /point-accounts/{id}/entries
POST /allocation-approvals</code></pre>
<p>Use expected versions on mutable operations. The close operation should be idempotent and produce an immutable close record. Tally generation references the snapshot, ballot set, method version, and software build. If a recount occurs, it creates another tally record rather than replacing the first.</p>
<h2 id="abuse">Plan for coercion, sybil attacks, and collusion</h2>
<p>Threats depend on stakes. Low-value community badges need different controls from public grants or corporate elections. Common risks include multiple accounts, credential sharing, vote buying, coordinated point farming, compromised administrators, recipient fraud, and denial of service near a deadline. Build a threat model before choosing controls.</p>
<p>Rate limits and authentication are necessary but not sufficient. Use eligibility evidence, account linking signals where lawful, manual review, independent observers, and post-round anomaly analysis. Avoid opaque fraud scores that cannot be appealed. A flagged account may be held for review without automatically disclosing the detection method.</p>
<h2 id="transparency">Publish rules before outcomes</h2>
<p>Participants should be able to read the round rules, eligible options, weighting, deadlines, quorum, privacy model, tally method, appeal process, and distribution timeline before acting. The API can expose a signed or hashed policy document linked from the round. Changes after opening should be rare, versioned, and clearly announced.</p>
<p>After close, publish the appropriate level of evidence: totals, turnout, invalid ballot counts, allocation summary, approval record, and audit statement. Do not publish private identities merely in the name of transparency. Transparency means the process can be understood and reviewed, not that every datum is public.</p>
<h2 id="conclusion">Governance APIs encode legitimacy</h2>
<p>A governance pool is not just a counter with a voting endpoint. It encodes who has authority, when that authority is measured, how it is exercised, and how outcomes become allocations or payments. Keep votes, points, awards, and donation budgets separate. Snapshot rules, preserve immutable ledgers, protect privacy, and require approvals for consequential changes.</p>
<p>Use the <a href="/pool-types/">Pool Types directory</a> to compare resource models and the <a href="/blog/credit-pool-api-ledger-quotas/">credit ledger guide</a> for reservation and accounting patterns.</p>
]]></content:encoded>
</item><item>
<title>Webhook and Endpoint Pool API Reliability: Retries, Signatures, Replay, and Delivery Logs</title>
<link>https://poolapi.com/blog/webhook-endpoint-pool-reliability/</link>
<guid isPermaLink="true">https://poolapi.com/blog/webhook-endpoint-pool-reliability/</guid>
<pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
<category>API Operations</category>
<description><![CDATA[How to build a multi-endpoint event delivery pool that survives duplicates, delays, rotations, partial outages, and tenant isolation failures.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A webhook or endpoint pool routes events to one or more registered destinations under shared delivery policy. It can support failover endpoints, regional destinations, event filters, fan-out, customer-specific URLs, internal service hooks, and agent callbacks. The difficulty is that HTTP delivery is not exactly once. Connections fail, responses disappear, endpoints time out, events arrive more than once, and destinations process messages out of order.</p>
<p>The reliable contract is therefore at-least-once delivery with stable event identifiers, authenticated messages, idempotent consumers, bounded retries, and replay tools. A pool adds selection and policy on top: which endpoint is eligible, whether to fan out or choose one, how to isolate tenant failures, and when to move an event to a dead-letter state.</p>
<h2 id="registry">Build an endpoint registry</h2><h3 id="registry-detail">Store policy with each destination</h3>
<p>Each endpoint record needs a durable identifier, tenant, URL, enabled state, event subscriptions, content format, signing secret reference, timeout, retry policy, region, health state, and ownership metadata. Store secrets in a secret manager and return only masked references through ordinary reads. URL changes should create a revision or audit event because they redirect sensitive data.</p>
<p>Validate endpoints before activation. A challenge-response flow can prove control of the destination. Enforce HTTPS for external traffic, block private or link-local addresses unless explicitly allowed, and protect against DNS rebinding and server-side request forgery. Re-resolve and validate destinations according to policy rather than trusting the registration-time IP forever.</p>
<h2 id="event-model">Use a stable event envelope</h2>
<p>An event should include a globally unique identifier, type, version, occurrence time, producer, tenant context, subject, data payload, and trace identifier. Keep the envelope stable while versioning the payload schema. An event identifier represents the business occurrence; each delivery attempt gets a separate attempt identifier. This distinction lets consumers deduplicate the event while operators inspect every retry.</p>
<p>Do not place secrets in the payload. Minimize personal data and support field-level redaction by subscription. Large payloads can be represented by a short-lived signed reference, but the consumer must be able to retrieve it within the replay window. If the reference expires before retries end, regenerate it without changing the event identity.</p>
<h2 id="signatures">Sign the bytes you send</h2>
<p>Calculate a signature over the exact transmitted body plus a timestamp and, optionally, endpoint identifier. Include the signature version and key identifier in headers. The consumer verifies the timestamp window, selects the correct key, computes the signature, and uses a constant-time comparison. Signing a parsed object instead of the bytes can fail when serialization changes whitespace or key order.</p>
<p>Support secret rotation with overlapping keys. An endpoint can accept the current and next key during a transition. Record which key signed each attempt. Never include the secret itself in a test event or dashboard. Provide example verification code, but make the protocol specification—not a single SDK—the source of truth.</p>
<h2 id="delivery">Define success and timeout precisely</h2>
<p>A delivery attempt starts when the worker sends the request and ends with a response, network error, or timeout. Define which status codes are successful. Most systems treat a timely 2xx as acceptance. Redirects should usually not be followed automatically because they can bypass destination validation. A timeout is unknown, not proof that the consumer did nothing; the consumer may have processed the event before the response was lost.</p>
<p>Stripe’s official <a href="https://docs.stripe.com/webhooks" rel="noopener">webhook documentation</a> notes that events can be retried and may not arrive in generated order, illustrating why receivers must track event identifiers instead of relying on a single delivery or timestamp. A pool should make the same expectations explicit and expose delivery history to endpoint owners.</p>
<h2 id="retries">Retry with backoff, jitter, and limits</h2><h3 id="retries-detail">Cap retries and retain evidence</h3>
<p>Retry transient failures with exponential backoff and jitter so a recovering endpoint is not flooded at once. Set a maximum attempt count or delivery window. Differentiate retryable network errors and 5xx responses from permanent failures such as an invalid URL or explicit 4xx rejection. Some 4xx responses may be temporary by contract, but do not retry every client error blindly.</p>
<p>Honor a reasonable <code>Retry-After</code> header where policy allows, while enforcing a maximum. Isolate queues by tenant or endpoint class so one failing customer cannot consume every worker. Use circuit breakers to pause an endpoint after repeated failure, then probe it before resuming full traffic. Surface the pause reason and next probe time.</p>
<h2 id="ordering">Do not promise global ordering casually</h2>
<p>Parallel workers, retries, and endpoint failures make strict ordering expensive. If ordering matters, define the scope: per subject, per account, or per stream. Attach a sequence number within that scope and let the consumer buffer or reject gaps. Strict serial delivery can let one stuck event block everything behind it, so use it only where the business operation requires it.</p>
<p>Consumers should be idempotent. Store processed event identifiers and make side effects conditional. When an event updates a versioned resource, compare resource version rather than arrival time. A later state event may arrive before an earlier one; applying both blindly can move the consumer backward.</p>
<h2 id="pool-routing">Choose fan-out, active-passive, or eligible-one routing</h2>
<p>Fan-out sends an event to every subscribed endpoint and tracks each delivery independently. Active-passive sends to a primary and uses a backup only under explicit conditions. Eligible-one routing chooses one endpoint based on region, health, capability, or load. These modes have different semantics and should be declared on the subscription.</p>
<p>Failover can duplicate side effects. If the primary processed the event but timed out, sending to a backup may process it again in a different system. Use a shared event identifier and require idempotency across failover destinations, or limit failover to events whose downstream action is safe. For command-like operations, a pull queue or transactional API may be better than webhooks.</p>
<h2 id="replay">Provide replay without creating a second event</h2>
<p>Replay should create new delivery attempts for the same event identifier. Operators can replay one event, a time range, an event type, or all failed events for an endpoint. Require scope and confirmation for broad replays. Apply current or historical payload policy deliberately; replaying an old event with a new schema can surprise consumers.</p>
<p>Keep a retention policy for payloads and delivery logs. If payload content expires, the system may retain metadata without being able to replay. State that clearly in the API. For sensitive events, shorter retention and customer-triggered deletion may be appropriate. A replay log records the actor, filter, time, and resulting attempts.</p>
<h2 id="dead-letter">Use a dead-letter state as an operational queue</h2>
<p>After the retry budget ends, move the delivery to a terminal failed or dead-letter state. This is not a trash can. It should have a reason, last response summary, endpoint state, payload availability, and recommended action. Endpoint owners can fix the destination and replay. Platform operators can detect systemic issues such as a signing change or DNS failure across many tenants.</p>
<p>Alerting should be proportional. One failed test event may not page anyone. A high failure ratio, sustained queue age, or failure across several endpoints may. Define service objectives around delivery acceptance within a time window and replay availability, while acknowledging that the destination controls final processing.</p>
<h2 id="api-shape">A webhook pool surface</h2>
<pre><code>POST /endpoints
POST /endpoints/{id}/verify
PATCH /endpoints/{id}
POST /endpoints/{id}/rotate-secret
GET  /events/{id}
GET  /events/{id}/deliveries
POST /deliveries/{id}/replay
POST /replays
GET  /dead-letter-deliveries</code></pre>
<p>List endpoints and delivery logs with cursor pagination. Filter by event type, state, time, endpoint, and tenant. Mask URLs according to role when they contain sensitive paths. Return response status, duration, attempt time, and a bounded response preview, not unlimited downstream content.</p>
<h2 id="observability">Trace every event through the pool</h2>
<p>Track publish-to-first-attempt latency, acceptance latency, retry count, queue age, response class, timeout, signature version, circuit state, and dead-letter rate. Measure by endpoint class and tenant without putting event identifiers in metric labels. Logs can carry event and attempt identifiers for detailed investigation.</p>
<p>Propagate a trace context when the consumer supports it, but do not trust returned trace fields as authoritative. Protect dashboards from payload exposure. Endpoint owners should see their own event metadata and attempts; platform staff should use elevated access for payload inspection with audit and redaction.</p>
<h2 id="conclusion">Reliable webhooks admit uncertainty</h2>
<p>A dependable webhook pool does not pretend delivery is exactly once or always ordered. It gives every event a stable identity, signs exact bytes, retries predictably, isolates failures, and makes replay and dead-letter operations visible. The endpoint registry and routing policy add the pool behavior, while idempotent consumers complete the contract.</p>
<p>See the <a href="/node-pools/">Node &amp; Endpoint Pools hub</a> and the companion <a href="/blog/api-marketplace-portal-design/">API portal design guide</a>.</p>
]]></content:encoded>
</item><item>
<title>Node Pool API Design: Registration, Health, Scheduling, Draining, and Failover</title>
<link>https://poolapi.com/blog/node-pool-api-health-scheduling/</link>
<guid isPermaLink="true">https://poolapi.com/blog/node-pool-api-health-scheduling/</guid>
<pubDate>Tue, 13 Jan 2026 00:00:00 +0000</pubDate>
<category>Compute Infrastructure</category>
<description><![CDATA[A control-plane blueprint for node identity, heartbeats, capabilities, leases, maintenance states, failure domains, and safe scheduling.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A node pool API manages a fleet of machines or runtime workers that can accept scheduled work. A node may be a physical server, virtual machine, edge device, container host, database replica, blockchain participant, or specialized inference worker. The control plane needs to know what each node can do, whether it is trustworthy, whether it is healthy, how much capacity remains, and when it should stop receiving work.</p>
<p>The design becomes reliable when identity, health, readiness, and capacity are separate concepts. A node can be authenticated but unhealthy. It can be healthy but intentionally draining. It can report free capacity while being incompatible with a workload. Compressing all of that into an “online” boolean produces unsafe scheduling and confusing operations.</p>
<h2 id="identity">Give every node a durable identity</h2><h3 id="identity-detail">Bind identity to attestation</h3>
<p>A node should have an immutable identifier independent of hostname or IP address. Registration binds that identifier to credentials, ownership, pool membership, labels, supported features, and enrollment evidence. Mutable network addresses and display names belong in metadata. Reimaging a machine may create a new node identity or a new incarnation number, depending on how the system handles trust and history.</p>
<p>Credentials should be short-lived or renewable and scoped to node operations. A node can update its own heartbeat and job state, but it should not add another node, change pool policy, or increase its capacity limit. Record the credential issuer, enrollment time, software version, and last rotation. A compromised node credential must be revocable without taking down the whole fleet.</p>
<h2 id="registration">Use an admission workflow</h2>
<p>Registration is not the same as admission. A newly enrolled node starts in a pending state while the control plane validates identity, software version, runtime configuration, capability inventory, and any required attestation. Automated checks can confirm that the node can pull an approved image, reach required services, enforce resource limits, and report telemetry. Only then should it become schedulable.</p>
<p>For contributor or marketplace nodes, add benchmarking and fraud controls. Self-reported hardware should not determine payment or high-value placement by itself. Keep measured performance, declared capability, and verified capability as separate fields. A node can remain visible to its owner while quarantined from production workloads.</p>
<h2 id="health">Model liveness, readiness, and health separately</h2>
<p>Liveness answers whether the node agent is communicating. Readiness answers whether the node is willing and configured to accept new work. Health summarizes whether hardware, runtime, storage, network, and required dependencies are within policy. A heartbeat can carry current observations, but the control plane should calculate the authoritative state using timestamps and rules.</p>
<p>Kubernetes documents nodes as worker machines managed by a control plane and containing services needed to run workloads; its <a href="https://kubernetes.io/docs/concepts/architecture/nodes/" rel="noopener">node concept</a> provides a useful reference for separating node status and management. A custom node pool can use similar principles without copying every Kubernetes object. The important part is a clear state model and a controller that reconciles desired state with observations.</p>
<h2 id="heartbeats">Design heartbeats for loss and delay</h2><h3 id="heartbeats-detail">Handle stale and partitioned nodes</h3>
<p>Heartbeats should include node identity, incarnation, monotonic sequence, observed time, agent version, readiness, active leases, and bounded health summaries. The server records receipt time. Do not trust the node clock as the only signal because clock skew can make stale data appear fresh. Use a grace period before marking a node unreachable, then a separate threshold before declaring work lost.</p>
<p>Heartbeats may be duplicated or arrive out of order. Ignore a lower sequence from the same incarnation. When a node restarts, increment the incarnation so old messages cannot overwrite new state. Keep payloads small and use separate endpoints or streams for detailed metrics. Heartbeat traffic must remain available during load spikes because it protects scheduling decisions.</p>
<h2 id="capacity">Publish allocatable capacity and active commitments</h2>
<p>Raw capacity is not the same as allocatable capacity. Reserve resources for the operating system, runtime, monitoring, and failure margin. Report active leases and measured use. The scheduler should account for reservations before a workload starts so two clients do not claim the same slot. Capacity updates and lease creation must be coordinated atomically in the control plane.</p>
<p>Represent heterogeneous resources explicitly: CPU, RAM, accelerator type and memory, local disk, network class, architecture, runtime, and attached services. Boolean labels are useful for features, while quantities need units and precision. A generic “size: large” label can be offered as a convenience but should resolve to a versioned shape.</p>
<h2 id="scheduling">Schedule with constraints and failure domains</h2>
<p>Filter nodes by hard requirements, policy, readiness, and current capacity. Score the remaining set for load, locality, warm caches, energy or price class, and failure-domain spread. Workloads with replicas should use anti-affinity across hosts, racks, zones, or providers. Tightly coupled workers may need affinity to a fast network or shared storage.</p>
<p>Return a decision reason and policy version to operators. When a job remains queued despite free aggregate capacity, the reason may be fragmentation, an incompatible accelerator, a region constraint, or a draining node. Exposing a stable reason code is more useful than showing a rapidly changing queue number.</p>
<h2 id="draining">Make maintenance a first-class state</h2>
<p>Draining means the node accepts no new ordinary work while existing work completes or moves. The drain request should include deadline, eviction policy, and reason. Some workloads can checkpoint and relocate. Others need graceful completion. The control plane tracks which leases remain and whether the deadline is at risk. At the end, the node becomes maintenance or offline rather than automatically returning to ready.</p>
<p>Cordon and drain are different operations. Cordon stops new placement. Drain also initiates workload handling. Emergency quarantine is different again: it may immediately revoke access, stop tasks, and block logs or artifacts until investigation. Keep those actions separate so routine maintenance does not trigger incident behavior and incidents do not masquerade as maintenance.</p>
<h2 id="leases">Reconcile leases after partitions</h2>
<p>Network partitions create uncertainty. The control plane may stop hearing from a node while the node continues running jobs. If the scheduler immediately reallocates stateful work, two copies may act at once. Use leases with fencing tokens. A replacement receives a newer token, and shared services reject writes from an older token. For jobs without shared side effects, retry may be simpler.</p>
<p>When connectivity returns, the node reports active leases and their tokens. The controller reconciles them against authoritative state. Unknown or expired work should be stopped. Do not let a stale node mark an already-replaced job as successful. Every state update must include the job, lease, node incarnation, and fencing value expected by the control plane.</p>
<h2 id="api-shape">A node-pool API surface</h2>
<pre><code>POST /node-enrollments
POST /nodes/{id}/admit
GET  /nodes/{id}
POST /nodes/{id}/heartbeats
POST /nodes/{id}/cordon
POST /nodes/{id}/drain
POST /nodes/{id}/quarantine
POST /node-leases
POST /node-leases/{id}/renew
POST /node-leases/{id}/release</code></pre>
<p>Administrative actions should require an expected version to prevent one operator from overwriting another. Heartbeat and lease endpoints can be optimized for machine traffic, but they still need authentication, bounded payloads, idempotency where appropriate, and protection against replay.</p>
<h2 id="observability">Observe the fleet as a pool</h2>
<p>Track ready capacity, reserved capacity, active use, fragmentation, unhealthy resources, unreachable nodes, drain duration, lease expiry, placement failures, and work lost by failure domain. A fleet with ninety percent “healthy” nodes can still be unable to serve a rare accelerator shape. Pool-level health should answer whether expected request classes can be satisfied.</p>
<p>Logs and traces should connect node enrollment, policy changes, scheduling decisions, job starts, heartbeat gaps, and recovery. Use metrics with bounded labels and retain high-cardinality identifiers in logs. Alert on missing heartbeats, repeated incarnation changes, capability drift, clock anomalies, and nodes that report capacity inconsistent with active leases.</p>
<h2 id="security">Assume a node can be compromised</h2>
<p>Limit a node’s control-plane permissions. Validate every job callback against the assigned lease. Keep tenant secrets short-lived and scoped to one job. Network policy should prevent a worker from reaching control-plane administration or unrelated tenant services. Signed images, measured boot, sandboxing, and dedicated pools can add assurance depending on risk.</p>
<p>Quarantine automatically when integrity checks fail, impossible capacity appears, or the node runs an unapproved version. Automation should be reversible by an authorized operator with an audit trail, but it should fail safe. Marketplace payouts or credits should not be finalized until usage evidence passes reconciliation.</p>
<h2 id="conclusion">Nodes are actors, not rows</h2>
<p>A node pool API is a distributed control system. Durable identity, admission, heartbeats, readiness, leases, fencing, draining, and reconciliation matter more than a polished list of servers. Design for delayed messages and partitions from the beginning. Make maintenance and quarantine explicit, and schedule only against verified, current capacity.</p>
<p>Continue with the <a href="/node-pools/">Node &amp; Endpoint Pools hub</a> or connect this design to the <a href="/blog/compute-pool-api-gpu-cpu-containers/">compute pool scheduler guide</a>.</p>
]]></content:encoded>
</item><item>
<title>Token and Stablecoin Pool API: Balances, Settlement, and Custody Boundaries</title>
<link>https://poolapi.com/blog/stablecoin-token-pool-api/</link>
<guid isPermaLink="true">https://poolapi.com/blog/stablecoin-token-pool-api/</guid>
<pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate>
<category>Digital Assets</category>
<description><![CDATA[A systems guide to pooled USDC, USDT, token, and asset APIs—with clear separation between ledgers, wallets, settlement, and custody.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A token pool API coordinates balances and movements across digital assets such as stablecoins, network tokens, tokenized assets, or internal settlement units. The architecture must make a critical distinction: an API ledger is not automatically the same thing as onchain ownership, and a displayed balance is not proof of custody. The system should state who controls keys, where assets are held, when a transfer is final, and which legal or operational entity is responsible.</p>
<p>This guide focuses on system design, not investment advice. A production implementation needs legal, compliance, security, accounting, and custody review appropriate to its jurisdiction and use case. The API can make boundaries explicit, but it cannot turn an unreviewed wallet design into a safe financial product.</p>
<h2 id="separate-layers">Separate asset, account, wallet, and ledger layers</h2><h3 id="separate-layers-detail">Keep balances and custody distinct</h3>
<p>The asset layer identifies what is being measured: asset code, issuer or contract, network, decimals, and status. The wallet layer identifies where onchain assets can be controlled or observed. The account layer represents a customer, organization, treasury, campaign, or settlement pool. The ledger records credits and debits attributed to those accounts. A single account may map to one wallet, many wallets, an omnibus wallet, or no wallet at all for offchain-only units.</p>
<p>Do not use a ticker alone as an identifier. The same symbol can exist on several networks or refer to unrelated contracts. Use a canonical asset identifier that includes network and contract or issuer context. Preserve the original chain transaction identifier and amount in base units. Convert for display at the edge, not in the source ledger.</p>
<h2 id="custody">State the custody model</h2>
<p>In a self-custody model, the user controls keys and the API prepares or observes transactions. In a custodial model, the operator or provider controls keys and authorizes movements. An omnibus model pools onchain assets while an internal ledger assigns beneficial balances. A segregated model maintains distinct addresses or wallets. Each model changes security, reconciliation, privacy, compliance, and failure behavior.</p>
<p>The API should expose the model in plain language and machine-readable fields where useful. Never suggest that an internal ledger entry is an onchain transfer. Distinguish pending deposit, detected transaction, confirmed deposit, available balance, reserved amount, submitted withdrawal, confirmed withdrawal, failed transfer, and reversed or adjusted ledger entry.</p>
<h2 id="stablecoins">Treat stablecoins as issuer- and network-specific assets</h2>
<p>Stablecoins can share a target denomination while differing in issuer, network support, contract behavior, compliance controls, redemption path, and operational risk. Circle’s developer documentation describes APIs for USDC balances, payments, transfers, and related products; its <a href="https://developers.circle.com/api-reference" rel="noopener">official API reference</a> is an example of keeping asset operations and authentication explicit. A generic token pool should preserve those provider-specific requirements rather than implying that all dollar-denominated tokens are identical.</p>
<p>Keep a supported-asset registry with status, decimals, network, deposit and withdrawal availability, minimums, confirmation policy, and provider route. Version changes. When an asset or network is paused, existing balances remain visible while new operations are blocked according to policy. Clients need a stable reason code and a clear distinction between temporary maintenance and permanent retirement.</p>
<h2 id="ledger">Use double-entry accounting for value movement</h2>
<p>A double-entry ledger records every value movement as balanced debits and credits between accounts. Deposits move value from a settlement or pending account into a customer account after confirmation. Withdrawals reserve value, then move it into an outgoing settlement account when submitted. Fees go to a fee account rather than disappearing from the transaction. Corrections use compensating entries.</p>
<p>Store amounts as integers in the asset’s smallest unit or fixed-precision decimals with strict scale. Never use binary floating point. Every entry should reference an operation, actor, idempotency key, asset identifier, and timestamp. Ledger balance, reserved balance, and available balance are different values and should be returned separately.</p>
<h2 id="deposits">Model deposits as observed events plus policy</h2>
<p>An address may receive a transaction, but availability depends on validation. Confirm the asset contract, destination, amount, network, and required finality. Screen or review transactions where required. Handle unsupported assets and wrong-network deposits through a defined exception process rather than silently crediting an approximate value. Deposit identifiers should be stable even if the same transaction contains several transfers.</p>
<p>Confirmation policy varies by network and risk tolerance. Do not hard-code one number into every asset. Record observed block, confirmations or finality state, and the policy version used to make funds available. Reorganizations or provider corrections require reversal logic. A deposit can be “seen” without being spendable.</p>
<h2 id="withdrawals">Reserve before sending a withdrawal</h2><h3 id="withdrawals-detail">Reserve, sign, broadcast, reconcile</h3>
<p>A withdrawal request first validates authorization, destination, asset support, amount, policy, and available balance. It then creates an atomic reservation. Approval, screening, and signing can occur afterward without exposing the funds to another concurrent request. Once a transaction is submitted, commit the reservation into an outgoing ledger movement and track the network state separately.</p>
<p>Use idempotency keys so retrying a withdrawal request cannot create a second transfer. Separate client request identifier, internal operation identifier, signing job, provider transfer identifier, and chain transaction identifier. Each serves a different purpose. Never infer failure solely from a client timeout; the operation may have progressed after the connection closed.</p>
<h2 id="settlement">Define settlement and finality</h2>
<p>“Settled” can mean posted to an internal ledger, accepted by a provider, included onchain, finalized by network policy, or redeemable through an issuer. Pick precise states and expose them. For cross-network or cross-provider flows, model each leg and its dependency. A user-facing aggregate status can summarize, but operators need the full state machine.</p>
<p>Reconciliation compares internal ledger entries, wallet balances, provider statements, and chain observations. Run it continuously or on a frequent schedule. Differences should create cases, not automatic balance edits. Common causes include delayed callbacks, provider fees, chain reorganizations, unsupported token receipts, decimal errors, and duplicate event processing.</p>
<h2 id="keys">Keep keys outside the general API service</h2>
<p>Signing should occur in a hardened wallet or key-management component with narrow policy. The general pool API submits an authorized signing request; it should not read raw private keys. Use multi-party approval or threshold controls where appropriate. Separate hot operational limits from larger treasury storage. Rotate API and provider credentials independently from wallet keys.</p>
<p>Policies can limit amount, asset, destination allowlist, velocity, time, tenant, and required approval. A compromised application key should not be able to drain the entire pool. Every signing decision and policy override needs an audit record. Logs must not contain seed phrases, private keys, full authentication tokens, or sensitive personal data.</p>
<h2 id="webhooks">Treat provider events as untrusted input</h2>
<p>Verify webhook signatures, timestamps, and endpoint secrets. Store the raw event securely, deduplicate by provider event identifier, and process it through an idempotent state transition. Events can arrive late, more than once, or out of order. When a critical event is missing, query the provider or network source of truth rather than guessing.</p>
<p>Do not allow a webhook payload to choose an arbitrary internal account. Map provider identifiers through your own trusted records. Validate asset, amount, and operation state before posting ledger entries. A duplicate “confirmed” event should return the existing result without a second credit.</p>
<h2 id="api-shape">A clear token-pool surface</h2>
<pre><code>GET  /assets
GET  /asset-accounts/{id}/balances
GET  /asset-accounts/{id}/ledger-entries
POST /deposit-addresses
GET  /deposits/{id}
POST /withdrawal-requests
GET  /withdrawal-requests/{id}
POST /transfers
GET  /reconciliation-runs/{id}</code></pre>
<p>Responses should identify asset, network, amount in base and display units, state, timestamps, account, operation, and related external identifiers. Sensitive destination or identity fields may need masking by role. Use cursor pagination and stable ordering for ledger queries.</p>
<h2 id="compliance">Make policy hooks explicit</h2>
<p>Depending on the product and jurisdiction, operations may require identity checks, sanctions screening, transaction monitoring, travel-rule data, consumer disclosures, licensing, tax reporting, or restrictions on asset and geography. The API should expose policy states such as pending review, approved, rejected, or additional information required without leaking internal screening logic.</p>
<p>Keep compliance decisions versioned and auditable. Manual reviewers should act through scoped tools that create signed decisions, not by editing database rows. Do not mix a reversible internal approval with an irreversible onchain action until every required gate has passed.</p>
<h2 id="conclusion">The boundary is the product</h2>
<p>A token or stablecoin pool API is trustworthy when it clearly separates the asset registry, wallet control, internal ledger, and settlement state. Use double-entry accounting, atomic reservations, idempotent operations, narrow signing authority, and continuous reconciliation. Preserve provider and network details rather than flattening them into a vague balance.</p>
<p>Review the broader <a href="/asset-pools/">Asset &amp; Token Pools hub</a> and the reliability patterns in <a href="/blog/webhook-endpoint-pool-reliability/">webhook endpoint pools</a>.</p>
]]></content:encoded>
</item><item>
<title>Compute Pool API for GPU, CPU, RAM, VPS, and Container Workloads</title>
<link>https://poolapi.com/blog/compute-pool-api-gpu-cpu-containers/</link>
<guid isPermaLink="true">https://poolapi.com/blog/compute-pool-api-gpu-cpu-containers/</guid>
<pubDate>Wed, 02 Jul 2025 00:00:00 +0000</pubDate>
<category>Compute Infrastructure</category>
<description><![CDATA[A scheduler-oriented guide to leases, resource shapes, queues, fairness, images, security, and telemetry for pooled compute capacity.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A compute pool API coordinates machines and schedulable capacity across GPU workers, CPU nodes, RAM-heavy hosts, VPS instances, containers, and specialized accelerators. Clients submit workload requirements instead of hard-coding a machine address. The control plane validates policy, finds capacity, creates a lease, and returns a route or execution handle. The data plane starts and monitors the workload.</p>
<p>Good compute pooling improves utilization and failure recovery, but it also concentrates risk. A mistake in the scheduler can overcommit memory, place sensitive workloads together, starve small tenants, or burn expensive accelerator time. The API should expose explicit resource shapes, lifecycle states, queue policy, and security boundaries rather than promising generic “compute.”</p>
<h2 id="resource-shape">Describe a workload with a resource shape</h2><h3 id="resource-shape-detail">Express minimums and preferences</h3>
<p>A resource shape defines the minimum and preferred CPU, memory, GPU count and type, local storage, network class, architecture, operating system, container runtime, and geographic constraints. Keep requests declarative. The client should say “one compatible accelerator with at least this memory” when exact hardware is not required, while still allowing a hard pin for validated workloads.</p>
<p>Separate allocatable capacity from raw machine totals. The operating system, runtime, monitoring, and safety margin consume resources. Accelerator topology matters: two GPUs on the same interconnect are different from two remote devices. RAM may be technically available but fragmented across NUMA boundaries. The member inventory should publish scheduler-relevant capacity, not marketing specifications.</p>
<h2 id="members">Register members with verified capabilities</h2>
<p>A node joins the pool through a controlled registration process. It presents an identity, attestation or enrollment credential, software version, resource inventory, labels, and supported runtime features. The control plane verifies the record before the node becomes schedulable. Heartbeats update health and current capacity, while periodic inventory reconciliation catches drift.</p>
<p>Do not trust a self-reported GPU model or memory total without validation when the pool spans untrusted contributors. In a private fleet, an authenticated agent may be enough. In a marketplace, stronger attestation, benchmarking, and isolation checks may be required. Keep “registered,” “verified,” “ready,” “draining,” “quarantined,” and “retired” as distinct states.</p>
<h2 id="leases">Allocate with leases</h2>
<p>A lease is a time-bounded claim on a resource shape. The scheduler atomically selects a member, reserves capacity, and returns a lease identifier with expiry. The client or execution controller commits the lease when startup begins. Heartbeats can extend long-running jobs. If the lease expires before commit, capacity returns to the pool. If the worker disappears, the control plane marks the lease uncertain until reconciliation confirms whether the workload is still running.</p>
<p>Leases prevent double scheduling and create a clean cancellation path. They also support reservations for scheduled work. A future reservation should specify a window and tolerance, not block a machine indefinitely without policy. Distinguish capacity held for a job from capacity actually consumed so utilization and billing remain accurate.</p>
<h2 id="containers">Treat the container runtime as a data-plane boundary</h2>
<p>Containers provide packaging and process isolation, but they are not a complete multi-tenant security model. Docker documents a client-server architecture in which the client communicates with the daemon through an API; the official <a href="https://docs.docker.com/get-started/docker-overview/" rel="noopener">Docker overview</a> is a useful reference for that control relationship. A compute pool should avoid exposing a privileged daemon directly to tenants. Put a narrow execution service between the pool API and the runtime.</p>
<p>Allow only approved image registries or signed images for sensitive environments. Resolve tags to immutable digests before scheduling. Set CPU, memory, process, filesystem, and network limits. Run as a non-root user where possible, drop unnecessary capabilities, and isolate secrets from the image. For stronger boundaries, consider dedicated virtual machines, sandboxed runtimes, or separate node pools based on workload trust.</p>
<h2 id="queue">Design the queue and fairness policy</h2>
<p>When demand exceeds capacity, the system needs more than first-in, first-out. A strict FIFO queue can let one large job block many small jobs. Weighted fair sharing, tenant quotas, priority classes, and maximum wait targets provide better control. State whether priority can preempt running work or only reorder queued work. Preemption is safe for checkpointable batch jobs and dangerous for stateful services unless recovery is designed.</p>
<p>Prevent starvation. Reserve some capacity for short jobs, enforce per-tenant concurrency, and age waiting jobs so they gain priority. Expose queue position cautiously because exact positions change quickly and can leak other tenants’ activity. A predicted start window or queue class is often more useful. Let clients cancel queued requests without creating a running job during a race.</p>
<h2 id="scheduling">Use filters, scores, and placement constraints</h2><h3 id="scheduling-detail">Separate eligibility from ranking</h3>
<p>The scheduler first filters nodes that cannot satisfy hard requirements. It then scores eligible members for locality, utilization, energy or price class, failure domain, warm image cache, and affinity. Keep anti-affinity rules for replicas that should not share a host or zone. Co-locate tightly coupled tasks only when the network and topology support it.</p>
<p>Version the scheduling policy and record the decision factors. Operators need to explain why a job landed on a specific node or waited despite apparent capacity. Capacity may be reserved, fragmented, quarantined, or incompatible with the image architecture. A transparent reason code reduces support load and helps clients choose better shapes.</p>
<h2 id="accelerators">Handle GPU and accelerator details explicitly</h2>
<p>GPU pools need fields for model or capability class, memory, driver and runtime compatibility, partitioning support, topology, and health. A device can be present but unsuitable because of error state, temperature, degraded links, or incompatible software. Run health checks that exercise the actual execution path, not just device enumeration.</p>
<p>Decide whether fractional GPU allocation is supported and what it means. Memory partitioning, time slicing, and process sharing have different isolation and performance characteristics. Expose them as distinct resource classes. Do not advertise fractional units unless the scheduler and runtime can enforce them. Record peak and average utilization so the pool can improve packing without guessing from requested limits alone.</p>
<h2 id="storage-network">Do not hide storage and network</h2>
<p>Compute jobs often fail because the scheduler focused on CPU and GPU while ignoring data. Include local scratch size, persistent volume needs, object-store access, ingress and egress constraints, and network locality. Large model weights may take longer to load than the computation itself. A warm cache signal can improve startup time, but it should be treated as a preference unless the job requires local data.</p>
<p>Network policy should limit which services a workload can reach. Egress controls protect credentials and reduce data exfiltration risk. If tenants bring datasets, specify encryption, retention, and cleanup. Leases should include storage lifecycle: what is ephemeral, what persists, and how the client retrieves outputs after the compute allocation ends.</p>
<h2 id="api-shape">A scheduler-oriented API surface</h2>
<pre><code>GET  /compute-pools
GET  /compute-pools/{id}/shapes
POST /compute-leases
POST /compute-leases/{id}/commit
POST /compute-leases/{id}/renew
POST /compute-leases/{id}/release
POST /jobs
GET  /jobs/{id}
POST /jobs/{id}/cancel
GET  /jobs/{id}/events</code></pre>
<p>A job request references a resource shape, image digest, command or workload manifest, input locations, network policy, maximum runtime, retry policy, and output destination. The response should distinguish queued, leased, starting, running, checkpointing, succeeded, failed, cancelled, and lost states. Every transition gets a timestamp and reason.</p>
<h2 id="observability">Measure allocation and execution separately</h2>
<p>Allocation metrics include queue time, scheduling attempts, unsatisfied constraints, lease expiry, and fairness by tenant. Execution metrics include startup time, runtime, CPU and memory pressure, accelerator utilization, retries, eviction, and exit reason. Capacity metrics include allocatable, reserved, used, fragmented, draining, and unhealthy resources by shape and failure domain.</p>
<p>Trace the path from request to scheduler decision, node admission, image pull, container start, and result upload. A generic “job failed” status is not enough. Clients need to know whether the image was invalid, capacity vanished, startup timed out, the process exited, or the node was lost. Operators need the same event sequence with infrastructure details.</p>
<h2 id="security">Protect the control plane</h2>
<p>Separate permissions for registering nodes, defining shapes, submitting jobs, viewing logs, retrieving outputs, and changing policy. A compromised worker should not be able to register more workers or alter quotas. Rotate node credentials and bind them to identity. Keep the scheduler’s credentials away from tenant containers. Validate every callback from a node against the lease and job it claims to update.</p>
<p>Use quotas for concurrent jobs, reserved GPU time, storage, and network egress in addition to credit balances. A well-funded tenant should not be able to exhaust all control-plane threads or queue memory. Rate-limit expensive scheduling searches and reject impossible shapes early.</p>
<h2 id="conclusion">Pool compute around enforceable contracts</h2>
<p>A compute pool API is a scheduler contract: declarative requirements in, verifiable allocation and execution events out. The strongest designs model capacity honestly, allocate with leases, isolate the runtime, define fairness, and keep accelerator, storage, and network constraints visible. Start with a few shapes and one trusted fleet before adding marketplace nodes or complex preemption.</p>
<p>Use the <a href="/compute-pools/">Compute Pools hub</a> for the broader resource map and the <a href="/blog/node-pool-api-health-scheduling/">node-pool guide</a> for registration and health patterns.</p>
]]></content:encoded>
</item><item>
<title>Prompt Pool API: Versioning, Evaluation, and Governance for AI Agent Workflows</title>
<link>https://poolapi.com/blog/prompt-pool-api-agent-workflows/</link>
<guid isPermaLink="true">https://poolapi.com/blog/prompt-pool-api-agent-workflows/</guid>
<pubDate>Wed, 19 Mar 2025 00:00:00 +0000</pubDate>
<category>AI &amp; Agents</category>
<description><![CDATA[How to turn prompts into governed, testable, versioned artifacts that agents and applications can retrieve without copying fragile text everywhere.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A prompt pool API manages prompts as reusable software artifacts rather than strings pasted into source files. It gives applications and agents a controlled way to discover a prompt, resolve a version, supply variables, inspect policy, and record which artifact produced an outcome. The value grows quickly when several teams reuse instructions, when prompts change often, or when an agent workflow combines system instructions, tool guidance, examples, and output schemas.</p>
<p>The central design decision is to keep prompt content, release state, and runtime variables separate. Content is versioned and reviewable. Release state says whether a version is draft, canary, active, deprecated, or retired. Runtime variables contain request-specific data. Mixing them makes review difficult and can leak customer content into the prompt catalog.</p>
<h2 id="artifact">Model a prompt as an artifact</h2><h3 id="artifact-detail">Version the rendered artifact</h3>
<p>A prompt record should include a stable prompt identifier, human-readable name, purpose, owner, task class, supported model families, variable schema, output contract, safety notes, tags, and lifecycle state. Each immutable version contains the actual instruction blocks, examples, tool descriptions, and change summary. The stable identifier lets callers follow the active release, while a version pin supports reproducibility.</p>
<p>Do not identify versions only by “latest.” Agents often run for minutes or hours, and a release can change midway through a workflow. Resolve the prompt version at workflow start and carry the version identifier through every trace. If a workflow intentionally upgrades between steps, record that transition explicitly. A content hash is useful for integrity, but it should complement a readable semantic or sequential version rather than replace it.</p>
<h2 id="composition">Compose layers with explicit precedence</h2>
<p>Many prompts contain several layers: platform policy, organization instructions, application role, workflow context, tool guidance, retrieved material, and user input. The pool API should define the order and which layers are mutable. Do not rely on string concatenation in random call sites. A composition manifest can name the components, versions, separators, variable mappings, and maximum size.</p>
<p>Keep trusted instructions distinct from untrusted content. User text and retrieved documents should be inserted into designated data fields or delimited blocks, not interpolated into an instruction line. Tool descriptions should be generated from a trusted registry where possible. This structure does not eliminate prompt injection, but it makes the trust boundary visible and testable.</p>
<h2 id="variables">Give variables a schema</h2>
<p>A variable schema should state names, types, required status, length limits, allowed values, sensitivity, and rendering behavior. Validate variables before rendering. Escape or delimit content according to the target format. A variable that contains JSON should be serialized as JSON rather than treated as plain prose. A list should render through a controlled template instead of allowing each caller to invent formatting.</p>
<p>Secrets generally do not belong in prompts. If a tool needs a credential, pass a scoped credential through the tool runtime, not through model-visible text. Mark sensitive variables so they can be redacted in traces. When storing rendered prompts for debugging, use policy-based retention and access controls; the catalog itself should contain templates and approved examples, not live customer payloads.</p>
<h2 id="evaluation">Require success criteria and evaluations</h2><h3 id="evaluation-detail">Keep test sets separate</h3>
<p>Prompt quality is contextual. Before changing wording, define what success means: exact schema validity, classification accuracy, citation behavior, tool choice, tone, refusal quality, or task completion. Anthropic’s official <a href="https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview" rel="noopener">prompt engineering overview</a> begins with success criteria and empirical tests, a useful principle for any prompt pool. Store the evaluation suite associated with each prompt and the result for each candidate version.</p>
<p>Tests should cover ordinary, adversarial, boundary, and regression cases. A prompt may improve average quality while breaking a critical edge case. Track results by model and adapter version because a prompt that works on one model may behave differently on another. Do not promote a version solely because it “looks better” in a playground; require evidence on representative inputs.</p>
<h2 id="release">Use a release lifecycle</h2>
<p>Draft versions are editable only by creating another immutable revision. Reviewers approve a candidate, then the release process can send a small traffic percentage to a canary. Promotion updates an alias such as <code>production</code> to point at the approved version. Rollback moves the alias back without deleting history. Deprecation warns callers that a version will be retired, while retirement prevents new resolutions but preserves old traces.</p>
<p>Separate content approval from deployment approval. A domain expert may approve correctness, while a platform owner approves data handling and cost impact. For high-risk prompts, require both. Record who approved, what evaluation suite ran, and which model pool policy was used. The API should return release metadata so callers know whether they resolved a canary or stable artifact.</p>
<h2 id="routing">Match prompts to model capabilities</h2>
<p>A prompt version can declare compatibility requirements: tool use, structured output, context size class, vision input, or reasoning tier. The model pool can then select eligible routes. Do not assume a prompt is portable merely because it contains plain text. Examples, formatting cues, tool syntax, and length may depend on a model family. Keep model-specific variants under one logical prompt when they serve the same task, and evaluate them separately.</p>
<p>Routing can also consider cost and latency. A classification prompt may have a fast production variant and a more capable review variant. The prompt pool selects the artifact variant; the model pool selects the runtime member. Keeping those decisions separate prevents the prompt catalog from turning into an ad hoc provider router.</p>
<h2 id="agent-workflows">Represent agent workflows as manifests</h2>
<p>An agent rarely uses one prompt. It may classify, plan, call tools, inspect results, recover from errors, and synthesize an answer. Define a workflow manifest that references prompt versions, tool policies, model requirements, budgets, and transition rules. Each step should have an identifier and an output contract. The runtime records step inputs, resolved artifacts, model route, tool calls, and result status.</p>
<p>Loops need explicit bounds. Set maximum iterations, cumulative credits, elapsed time, and tool calls. A prompt should not be able to rewrite those limits. Recovery prompts should be separate artifacts with their own evaluations, not hidden strings inside exception handlers. This makes agent behavior reviewable and lets teams test failure paths before production.</p>
<h2 id="api-shape">A compact prompt API surface</h2>
<pre><code>GET  /prompts?task=invoice_extraction&amp;state=active
GET  /prompts/{prompt_id}
GET  /prompts/{prompt_id}/versions/{version}
POST /prompt-resolutions
POST /prompt-evaluations
POST /prompt-releases
POST /prompt-aliases/{alias}/promote
POST /prompt-aliases/{alias}/rollback</code></pre>
<p>A resolution request can include the logical prompt, alias or version constraint, model capability context, and variable schema version. The response should identify the resolved version, content hash, required variables, output contract, and permitted model profiles. Rendering may happen server-side or in a trusted SDK. If rendered server-side, avoid returning sensitive variables in logs or cache keys.</p>
<h2 id="caching">Cache immutable versions safely</h2>
<p>Immutable prompt versions are excellent cache candidates. Use the version or content hash as the cache key and publish cache-control rules appropriate to the client. Aliases such as <code>production</code> need shorter caching or explicit invalidation because their target changes. A resolution response can include both the alias and resolved version so the caller can cache the immutable artifact after one lookup.</p>
<p>Be careful with rendered prompts. Caching rendered output may mix tenant data if the key omits a variable or policy dimension. Prefer caching the template and performing rendering in a tenant-scoped runtime. If server-side rendering is necessary, include every input and policy version in the key and encrypt sensitive cached content.</p>
<h2 id="governance">Apply governance without freezing iteration</h2>
<p>Prompt governance should make change safer, not create a paperwork wall. Use lightweight review for low-risk internal prompts and stricter gates for prompts that control external actions, regulated data, financial decisions, or customer-facing claims. Require owners and expiration reviews so abandoned prompts do not remain active indefinitely. Provide diffs that highlight changed instructions, examples, variable schemas, and tool permissions.</p>
<p>Access roles can include reader, editor, reviewer, releaser, and auditor. A runtime needs read access to approved versions, not edit access. A developer can create a draft without promoting it. Emergency rollback should be possible with a narrow permission and an audit event. Avoid giving every agent the ability to enumerate the entire prompt catalog; authorize by application, task, or namespace.</p>
<h2 id="observability">Connect outcomes to exact artifacts</h2>
<p>Every execution trace should include prompt identifier, version, content hash, workflow step, model route, evaluation suite version, and output-validation result. Keep user content out of ordinary metrics. Aggregate success by prompt version and model profile to detect regressions. A sudden increase in schema failures after a model adapter update may not require a prompt change, and the trace data should make that distinction clear.</p>
<p>Monitor resolution failures, deprecated-version use, canary performance, render errors, variable validation, context truncation, and rollback frequency. Set alerts for workflows using retired prompts or unreviewed drafts. Review low-volume prompts separately so their failures are not hidden by high-volume successes.</p>
<h2 id="conclusion">Prompts deserve software discipline</h2>
<p>A prompt pool API creates a dependable path from authored instruction to measured runtime outcome. Version content immutably, validate variables, compose layers with explicit trust boundaries, promote through a release lifecycle, and associate every version with evaluations. For agents, add workflow manifests, budgets, loop limits, and exact tracing.</p>
<p>The payoff is not merely reuse. It is the ability to answer which prompt ran, why it was approved, which model executed it, and whether it met the contract. Pair this design with the <a href="/model-pools/">Model &amp; Prompt Pools hub</a> and the <a href="/blog/model-pool-api-llm-routing/">LLM routing guide</a>.</p>
]]></content:encoded>
</item><item>
<title>Model Pool API Architecture for LLM Routing, Fallbacks, and Agent Workloads</title>
<link>https://poolapi.com/blog/model-pool-api-llm-routing/</link>
<guid isPermaLink="true">https://poolapi.com/blog/model-pool-api-llm-routing/</guid>
<pubDate>Fri, 08 Nov 2024 00:00:00 +0000</pubDate>
<category>AI &amp; Agents</category>
<description><![CDATA[A practical blueprint for routing across LLMs without hiding capability differences, cost controls, reliability signals, or evaluation evidence.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A model pool API gives applications and agents one control surface for selecting among language models, embedding models, image models, or other inference endpoints. It can improve resilience and utilization, but only when it treats models as differentiated resources rather than interchangeable boxes. Models vary in capabilities, context handling, latency, output controls, tool support, safety behavior, geography, and data policy. A router that hides those differences may be simple to call but difficult to trust.</p>
<p>The goal is not a universal lowest-common-denominator API. The goal is a stable envelope for requirements, policy, and observability, combined with explicit provider or model extensions where needed. Clients describe the job and constraints. The control plane selects an eligible route. The data plane executes the request. The response records what ran, which policy chose it, and how the result was measured.</p>
<h2 id="inventory">Build a capability inventory</h2><h3 id="inventory-detail">Describe capabilities consistently</h3>
<p>Start by describing each member with more than a display name. Useful fields include modality, supported tools, structured-output support, streaming, context class, input and output formats, latency class, regional availability, data-retention policy, safety profile, status, and provider-specific configuration. Avoid encoding volatile commercial values directly into a permanent capability label. Price and availability can change; capability contracts should remain stable enough for clients to target.</p>
<p>Distinguish declared capability from verified capability. A provider may state that a model supports a feature, while your integration tests prove that the feature works with your adapter and schema. Store the source and last verification time. Disable a capability for routing when tests fail, even if the model remains available for other uses. This prevents a “healthy” endpoint from receiving work it cannot complete correctly.</p>
<h2 id="request-envelope">Use a requirement envelope</h2>
<p>A normalized request should state intent and constraints: task class, required modalities, tool needs, structured-output schema, maximum latency class, cost ceiling, geography, data policy, and quality tier. Preserve the original prompt and provider-specific options in a controlled extension area. Do not force every provider parameter into one global namespace; name collisions and subtly different semantics will accumulate.</p>
<p>Separate hard requirements from preferences. “Must remain in region X” is a hard filter. “Prefer the fastest model” is a ranking preference. “Use this exact model” is a pin that bypasses normal routing but should still pass authorization and health checks. This distinction makes failures actionable. The router can report that no model met a hard constraint instead of silently violating it in the name of fallback.</p>
<h2 id="selection-evals">Route with evaluation evidence</h2>
<p>Selection should combine static policy with measured performance on representative tasks. Build task-specific evaluations for correctness, format adherence, tool execution, refusal behavior, latency, and cost. The official OpenAI guidance on <a href="https://developers.openai.com/api/docs/guides/model-selection" rel="noopener">model selection</a> emphasizes using accuracy and cost evidence rather than choosing by name alone. A pool can operationalize that discipline by attaching evaluation results and freshness to each eligible route.</p>
<p>Do not collapse evaluation into one permanent score. A model can excel at extraction and underperform on long-form reasoning. A fast model may be appropriate for an interactive agent step but not for a high-stakes final review. Store scores by task suite and version, then let routing policies reference the suites relevant to the request. When prompts or adapters change, rerun the affected evaluations.</p>
<h2 id="routing-policy">Make routing policy layered</h2>
<p>A practical policy usually has four phases. First, authorize the tenant and task. Second, filter by hard capability and policy constraints. Third, remove unhealthy or saturated members. Fourth, rank candidates by a weighted objective such as expected quality within a latency and budget envelope. The policy version, candidate set, and winning reason should be traceable for operators.</p>
<p>Layer organization policy above application preference. An application may prefer a provider, but the organization may prohibit that provider for a data class or region. A tenant may have a monthly budget, while a request has a per-call ceiling. Resolve those constraints deterministically and document precedence. Never let a user-supplied “provider” field bypass a policy that the server would otherwise enforce.</p>
<h2 id="fallbacks">Design fallbacks as transitions, not guesses</h2><h3 id="fallbacks-detail">Bound retries and substitutions</h3>
<p>A fallback is safe only when the next model can satisfy the original hard requirements and when retrying the operation will not duplicate side effects. Classify failures into pre-execution failures, partial responses, provider errors, policy rejections, and invalid model output. A connection failure before any tokens are processed may be safe to reroute. A tool-calling response that already triggered an external action is not.</p>
<p>Set a retry budget and a maximum route depth. Record every attempted member in the trace. Avoid fallback loops between adapters. For streaming, decide whether the client can receive a restarted stream and how the event identifies a new attempt. In many cases it is better to return a clear error than to concatenate output from two models without disclosure.</p>
<h2 id="normalization">Normalize carefully at the edges</h2>
<p>Adapters translate the shared request envelope into provider calls and translate provider responses back into a stable event model. Normalize common concepts such as text chunks, tool calls, usage, finish reasons, safety outcomes, and errors. Preserve raw provider metadata in a namespaced field for diagnostics. Version adapters independently from routing policy so a provider API change does not require rewriting selection logic.</p>
<p>Structured output deserves validation after normalization. If a model returns JSON, validate it against the requested schema and classify repair attempts as separate steps. Do not report success merely because the provider returned a 200 response. The model pool’s success criterion is the application contract, not transport-level availability.</p>
<h2 id="usage-budget">Integrate credits and quotas</h2>
<p>Model routing should consult the tenant’s credit and quota policy before execution. Reserve a conservative maximum, send the request, then commit measured usage and release the remainder. Normalize input, output, cached, tool, and image units without discarding provider-native values. A credit conversion can make budgets comparable, but operators still need raw usage for reconciliation.</p>
<p>Use budget as a constraint rather than always selecting the cheapest model. A quality floor may eliminate low-cost candidates. A latency ceiling may remove an otherwise efficient route. Agent workflows need cumulative budgets across many steps; the route decision should know the remaining workflow budget, not only the current call limit. Stop or degrade gracefully when the budget is nearly exhausted.</p>
<h2 id="agents">Support agent loops explicitly</h2>
<p>Agents make repeated model calls, invoke tools, and update state. Give every workflow a durable identifier and every model call a step identifier. Apply per-step and whole-workflow limits. A router can choose a fast model for classification, a tool-capable model for planning, and a stronger model for final synthesis, but the policy should be visible and evaluable as a workflow rather than a pile of independent calls.</p>
<p>Guard against recursive escalation. An agent should not be able to request a more privileged model, a larger context, or a higher budget simply by placing instructions in user content. Tool permissions and model routing constraints belong in trusted control-plane state. Log model and tool transitions so investigators can reconstruct how an agent reached an outcome.</p>
<h2 id="privacy">Preserve data and security boundaries</h2>
<p>Attach a data classification to each request and a handling policy to each route. The router can then filter models by region, retention, training use, encryption, and contractual requirements. Do not send the full request to several providers merely to race them unless the user and policy permit that disclosure. Hedged requests can reduce tail latency but multiply exposure and cost.</p>
<p>Store provider credentials in a secret manager and issue adapters only the scopes they require. Redact prompt content from ordinary logs. Keep a configurable path for secure debugging with explicit access and retention. Response metadata may identify the provider and model to authorized clients, but internal scores, negotiated rates, and other tenants’ utilization should remain private.</p>
<h2 id="observability">Observe quality, not just uptime</h2>
<p>Track route success, adapter errors, time to first token, total latency, usage, cost-equivalent units, schema validity, tool-call validity, fallback count, and evaluation outcomes. Slice by task class, model, provider, policy version, region, and tenant class. Average latency alone can hide severe tails, and transport success can hide unusable responses.</p>
<p>Run canary traffic when adding a model or adapter version. Compare it against a control on representative traffic where policy allows. Keep automatic rollback criteria for error rate and contract validity. Quality regressions may require sampled human review or offline evaluation, so combine operational metrics with task-level evidence rather than pretending one dashboard can capture every outcome.</p>
<h2 id="api-shape">A reference route request</h2>
<pre><code>POST /model-routes
{
  "task": "structured_extraction",
  "requirements": {
    "modalities": ["text"],
    "structured_output": true,
    "region": "us",
    "max_latency_ms": 4000
  },
  "preferences": {
    "quality_tier": "balanced",
    "cost_ceiling_credits": 120
  },
  "workflow_id": "wf_01J..."
}</code></pre>
<p>The response can provide a route token, selected capability profile, policy version, lease expiry, and a data-plane endpoint. For direct gateway execution, the same endpoint may accept the prompt payload, but keep control-plane decisions and provider adapter telemetry distinct internally.</p>
<h2 id="conclusion">The router is a policy product</h2>
<p>A model pool succeeds when it gives clients consistency without erasing important differences. Build the capability inventory, separate hard constraints from preferences, route with evaluation evidence, and make fallbacks explicit. Tie every call to credits, policy, and traces. The result is a reliable control layer for applications and agentic workflows—not a black box that swaps model names and hopes for equivalent behavior.</p>
<p>Next, explore <a href="/model-pools/">model and prompt pools</a> or the companion guide to <a href="/blog/prompt-pool-api-agent-workflows/">versioned prompt pool APIs</a>.</p>
]]></content:encoded>
</item><item>
<title>Credit Pool API Design: Ledgers, Quotas, Reservations, and Safe Retries</title>
<link>https://poolapi.com/blog/credit-pool-api-ledger-quotas/</link>
<guid isPermaLink="true">https://poolapi.com/blog/credit-pool-api-ledger-quotas/</guid>
<pubDate>Thu, 27 Jun 2024 00:00:00 +0000</pubDate>
<category>API Operations</category>
<description><![CDATA[How to model pooled API credits without double-spend, ambiguous balances, or retry bugs—and how to separate limits from accounting.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A credit pool API lets several users, applications, agents, or projects draw from a shared allowance while preserving clear ownership and accounting. The “credit” might represent model tokens, prepaid API units, compute seconds, storage operations, internal reward points, or a negotiated service allowance. The hard part is not subtracting a number. The hard part is defining when value becomes unavailable, when it is finally consumed, and how every change can be explained later.</p>
<p>The safest architecture separates the policy that limits activity from the ledger that records value. Quotas answer whether a request is permitted during a period. Balances answer how much value remains. Reservations prevent concurrent requests from spending the same units. Usage records describe measured activity. Adjustments correct mistakes without erasing history. Treating all five as a single mutable counter creates race conditions and makes disputes nearly impossible to resolve.</p>
<h2 id="unit">Define the unit before the endpoint</h2><h3 id="unit-detail">Choose precision and conversion rules</h3>
<p>A credit needs a stable unit and scope. “One credit” is meaningless unless the system states what it buys, where it can be used, whether it expires, and whether it is transferable. Some products use provider-native units such as tokens or GPU-seconds. Others use an internal unit that converts different resources into a common budget. Internal units simplify purchasing but require a versioned conversion table so historical usage can be reconstructed after prices or weights change.</p>
<p>Do not silently mix monetary balances, promotional points, and operational quotas. They may appear together in a user interface, but they have different legal and accounting meanings. A promotional grant may expire and be nonrefundable. A paid balance may require separate treatment. A rate limit resets with time and usually is not an asset at all. Store them as distinct accounts or buckets, then expose an aggregated view only when the API explains the composition.</p>
<h2 id="ledger">Use an append-only ledger</h2>
<p>The ledger should record immutable entries such as grant, purchase, reservation, commit, release, expiration, refund, and adjustment. Each entry includes a unique identifier, account, amount, unit, timestamp, actor, reason, related request, and idempotency key. A running balance may be materialized for speed, but it should be verifiable from the entries. Corrections should be new compensating entries rather than edits to old rows.</p>
<p>This model supports audit and incident response. If a balance appears wrong, operators can trace the exact sequence. If a conversion rate was misconfigured, affected entries can be identified by rule version and corrected. If a user retries a request, the idempotency key proves whether the original operation succeeded. The ledger also creates a clean foundation for exports, invoicing, internal chargeback, and tenant-level reporting.</p>
<h2 id="reservation">Reserve, commit, and release</h2>
<p>Many API calls have unknown final cost. An LLM request may produce fewer output tokens than the maximum. A compute job may finish early. The credit pool should reserve a conservative amount before work begins, then commit the measured amount and release the remainder. The available balance is therefore the posted balance minus active reservations, not simply the sum of grants and charges.</p>
<p>Reservations need an expiration, owner, maximum amount, and state. A client creates a reservation with an idempotency key. The server atomically checks eligibility and available balance, then moves the amount into reserved state. When the data-plane job reports actual usage, the server commits up to the reserved amount. If actual use can exceed the reservation, the contract must define whether the call is stopped, an overage is allowed, or a second authorization is required.</p>
<h2 id="quotas">Keep quotas independent from balances</h2><h3 id="quotas-detail">Layer limits without double counting</h3>
<p>A customer can have a large balance and still need a request-per-minute limit to protect infrastructure. Conversely, a free sandbox may have no stored balance but a daily quota. Model quotas as policy objects with dimensions such as request count, input units, output units, concurrent jobs, or spend-equivalent units. Apply them to an organization, project, API key, user, model class, or endpoint, and state which rule wins when several scopes overlap.</p>
<p>Provider guidance on <a href="https://developers.openai.com/api/docs/guides/rate-limits" rel="noopener">API rate limits</a> illustrates why clients need explicit limit dimensions and retry behavior. A credit-pool gateway should preserve useful upstream signals but normalize them into its own contract. Return remaining quota, reset timing, and a stable reason code where appropriate, while avoiding headers that reveal another tenant’s activity or an internal provider limit that clients cannot act on.</p>
<h2 id="idempotency">Make every money-like mutation idempotent</h2>
<p>Networks fail at inconvenient moments. The server may commit a charge and the response may be lost, causing the client to retry. Without idempotency, the second request creates a duplicate entry. Require an idempotency key on reservation, commit, release, grant, transfer, and adjustment operations. Bind the key to the tenant and operation type, persist the response, and reject reuse with a different payload.</p>
<p>Idempotency does not replace concurrency control. Two different keys can still race for the same balance. The database operation that verifies available credits and creates a reservation must be atomic. A serializable transaction, conditional update, or carefully designed ledger lock can provide this guarantee. For very high volume, partition accounts and use a single writer per partition, but preserve a durable sequence so recovery can replay events deterministically.</p>
<h2 id="account-model">Use accounts and buckets, not one global number</h2>
<p>An organization may need separate pools for production, development, departments, campaigns, or agent teams. Represent these as accounts with parent-child relationships and explicit funding rules. A child account may receive a fixed grant, borrow from a parent up to a cap, or share a common parent balance while retaining its own quota. Make the inheritance rules visible; hidden fallback creates surprising spend.</p>
<p>Within an account, buckets can encode source and restrictions: paid, promotional, provider-specific, expiring, or project-restricted. Define deterministic consumption order, such as earliest expiry first, then promotional, then paid. Record which bucket funded each commit. If a request can draw from multiple buckets, the ledger should show the split rather than collapsing it into one charge.</p>
<h2 id="api-shape">A useful endpoint surface</h2>
<pre><code>GET  /credit-accounts/{id}
GET  /credit-accounts/{id}/balance
GET  /credit-accounts/{id}/entries
POST /credit-reservations
POST /credit-reservations/{id}/commit
POST /credit-reservations/{id}/release
POST /credit-adjustments
GET  /quota-status?account_id=...&amp;resource=...</code></pre>
<p>Keep the balance response compact but include posted, reserved, available, unit, and an “as of” timestamp. Ledger queries should support time ranges, entry types, related request identifiers, and cursor pagination. Never use floating-point arithmetic for credit units. Store integers in the smallest practical unit or fixed-precision decimals with an explicit scale.</p>
<h2 id="expiry-refunds">Model expiration, refunds, and negative states deliberately</h2>
<p>Expiration should be a ledger event generated by a clear rule, not a nightly overwrite of the balance. If credits can be refunded, define whether the original bucket is restored, a new bucket is created, or the refund becomes a separate payable balance. Decide whether negative balances are prohibited, tolerated within a limit, or represented as debt. The API should not drift below zero because two requests arrived together.</p>
<p>Reservations that expire while work is still running require a policy. The system can extend leases through heartbeats, stop the job, or permit a controlled grace window. Whatever the rule, the data plane must know the authorization state. A background sweeper alone is not enough if a long-running worker can continue consuming after its budget has been released to someone else.</p>
<h2 id="reporting">Design reporting from the ledger outward</h2>
<p>Useful reports include usage by project, key, model class, provider, endpoint, cost center, and time. Keep raw usage units alongside converted credits so operators can inspect the conversion. Attach the policy and price-table version used for each charge. Aggregate asynchronously for dashboards, but let the underlying entries remain the source of truth.</p>
<p>Privacy and access control matter. A team manager may see project totals without seeing prompt content or another team’s API key. Finance may see monetary equivalents without payloads. Support staff may need a trace identifier and entry history but not credentials. Define these views as roles rather than hand-building filters in every report.</p>
<h2 id="failure-testing">Test the failure cases first</h2>
<p>Simulate duplicate commits, delayed usage reports, out-of-order events, expired reservations, partial provider failures, database failover, and adjustments during active work. Assert that the ledger remains balanced and that the same idempotency key always returns the same result. Reconcile the sum of data-plane usage against committed ledger entries and alert on drift rather than assuming every callback arrives.</p>
<p>A credit pool becomes trustworthy when every unit has a reason, every mutation is retry-safe, and capacity cannot be spent twice. Begin with one unit, one account hierarchy, and one reserve-commit-release path. Add conversions, transfers, and marketplace settlement only after the core ledger survives concurrency and recovery tests.</p>
<h2 id="conclusion">A credit pool is an accounting system with an API</h2>
<p>The endpoint design is important, but the durable value comes from immutable history, atomic reservations, explicit quota policy, and deterministic reconciliation. Keep money-like balances separate from rate limits and reward points. Make retry behavior part of the contract. With those foundations, the same pattern can govern AI tokens, cloud credits, compute time, internal points, or prepaid API usage without losing explainability.</p>
<p>See the broader <a href="/credit-pools/">Credit Pool API guide</a> and the related <a href="/blog/model-pool-api-llm-routing/">model-pool routing architecture</a>.</p>
]]></content:encoded>
</item><item>
<title>What Is a Pool API? A Practical Architecture for Shared Digital Resources</title>
<link>https://poolapi.com/blog/what-is-a-pool-api/</link>
<guid isPermaLink="true">https://poolapi.com/blog/what-is-a-pool-api/</guid>
<pubDate>Wed, 14 Feb 2024 00:00:00 +0000</pubDate>
<category>Pool API Fundamentals</category>
<description><![CDATA[A field guide to the resource model, lifecycle, control plane, allocation rules, and reliability patterns behind APIs that coordinate shared pools.]]></description>
<content:encoded><![CDATA[
<p class="article-intro">A pool API is an interface for discovering, reserving, using, measuring, and releasing a shared set of resources. The resources might be AI credits, language models, prompts, GPU workers, database connections, blockchain assets, webhook endpoints, domain names, or community points. What makes the design a pool is not the noun. It is the operating model: many eligible resources sit behind one policy boundary, and clients ask the system to choose or allocate an appropriate member.</p>
<p>The useful abstraction is therefore broader than a list endpoint. A production pool API needs to answer who may consume the pool, what each resource can do, how allocation decisions are made, how concurrent requests are handled, what happens when a member fails, and how usage is reconciled afterward. The API is the contract that turns those operational rules into predictable behavior for applications and agents.</p>
<h2 id="pool-as-system">Treat the pool as a system, not a collection</h2><h3 id="pool-as-system-detail">Normalize the member contract</h3>
<p>A collection says, “Here are several objects.” A pool says, “Here are several objects that can satisfy related work under shared rules.” That distinction changes the data model. Every member needs a stable identifier, capability metadata, state, ownership or tenancy information, health signals, capacity, and policy attributes. The pool itself needs selection rules, limits, lifecycle settings, and an audit trail. Without those fields, clients eventually encode hidden assumptions that break as the pool grows.</p>
<p>For example, a model pool may include fast text models, high-reasoning models, embedding models, and providers with different geographic or data-handling constraints. A compute pool may mix GPU shapes, CPU-only nodes, and memory-heavy workers. A token pool may hold balances across multiple assets or networks. The API should expose enough normalized metadata for safe selection while preserving provider-specific fields in an extension object rather than flattening everything into the least capable common denominator.</p>
<h2 id="canonical-lifecycle">Define one canonical resource lifecycle</h2>
<p>Most pooled systems become easier to reason about when they use a small, explicit lifecycle: discover, qualify, reserve, commit, consume, release, and reconcile. “Discover” returns eligible members. “Qualify” applies hard constraints such as region, model capability, available memory, or asset type. “Reserve” creates a time-bounded claim. “Commit” confirms that work will begin. “Consume” records actual use. “Release” returns unused capacity. “Reconcile” compares the reservation with measured outcomes and writes final accounting entries.</p>
<p>Not every pool needs every step, but the vocabulary should remain stable. A lightweight prompt pool may skip reservation because prompts are read-only. A GPU pool should not. A credit pool may reserve units before sending a model request, then commit the exact amount after usage is known. A webhook endpoint pool may reserve nothing but still needs health, routing, delivery, and replay states. The design goal is explicit transitions rather than overloaded status strings that mean different things to different clients.</p>
<h2 id="control-data-plane">Separate the control plane from the data plane</h2>
<p>The control plane manages membership, metadata, policy, limits, credentials, health, and allocation. The data plane performs the actual work: calling the selected model, running the container, sending the webhook, moving the asset, or querying the database. Keeping them separate limits blast radius and keeps the pool API from becoming a monolith. A client can request an allocation from the control plane and receive a lease, signed reference, temporary credential, or routing token for the data plane.</p>
<p>This separation also clarifies security. Administrative calls that add a node or change a quota deserve stronger permissions than calls that request a short-lived allocation. Data-plane credentials can be scoped to one resource and one time window. Audit records can distinguish “who changed the rules” from “who used the pool.” When an incident occurs, operators can disable allocation while preserving read-only visibility into the pool and its history.</p>
<h2 id="http-contract">Use familiar HTTP semantics</h2>
<p>A pool API should look unsurprising to clients. Resources can live under paths such as <code>/pools</code>, <code>/pools/{pool_id}/members</code>, <code>/allocations</code>, <code>/leases</code>, and <code>/usage-events</code>. Use GET for safe retrieval, POST when creating reservations or actions, PATCH for partial policy changes, and DELETE when removing a member or cancelling an uncommitted lease. The <a href="https://www.rfc-editor.org/rfc/rfc9110.html" rel="noopener">IETF HTTP Semantics standard</a> is a useful baseline for method behavior, status codes, caching, and conditional requests.</p>
<p>Design errors as machine-readable objects with stable codes. “No member matched the requested capability” is different from “eligible members exist but all capacity is reserved.” A client may relax a preference in the first case, retry later in the second, and stop immediately on an authorization failure. Put a correlation identifier on every response and propagate it into downstream calls so one allocation can be traced across the control plane and data plane.</p>
<h2 id="selection">Make selection policy visible and testable</h2>
<p>Selection is the heart of the system. Start with hard filters, then rank the remaining candidates. Hard filters might include tenant access, minimum memory, supported model feature, network, compliance region, or endpoint health. Ranking can consider latency, price class, available capacity, fairness, affinity, recent failures, and operator-defined weights. Keep the policy versioned. If an allocation looks wrong, the team should be able to reproduce the decision with the member snapshot and policy version used at that moment.</p>
<p>Avoid publishing a magical “best” score without components. Return a concise decision explanation to authorized operators: which filters removed candidates, which signals affected ranking, and why the winner was chosen. Do not reveal other tenants’ sensitive usage or provider credentials. For end-user clients, a simpler reason code is enough, such as <code>lowest_latency_within_budget</code> or <code>preferred_region_capacity_available</code>.</p>
<h2 id="concurrency">Plan for concurrency, leases, and idempotency</h2><h3 id="concurrency-detail">Protect every state transition</h3>
<p>Shared capacity creates races. Two clients can read the same available balance or worker slot and both try to claim it. Reservations should therefore be atomic and represented by leases with explicit expiration. The API must define whether expired leases are released automatically, whether they can be renewed, and what happens when a client commits just as the lease expires. Use idempotency keys for create and commit operations so a network retry does not double-allocate or double-charge.</p>
<p>Optimistic concurrency controls are also valuable for administrative updates. Include a version or entity tag on the pool and member records. A policy update based on an old version should fail cleanly instead of overwriting a newer change. For ledgers, prefer immutable entries plus corrective entries over in-place edits. The current balance can be derived or materialized, while the event history preserves how it was reached.</p>
<h2 id="reference-shape">A compact reference response</h2>
<pre><code>{
  "allocation_id": "alloc_01J...",
  "pool_id": "pool_models_global",
  "member_id": "member_reasoning_02",
  "state": "reserved",
  "lease_expires_at": "2026-10-20T19:45:00Z",
  "capabilities": ["text", "structured_output"],
  "decision": {
    "policy_version": "route-v12",
    "reason": "capability_match_within_latency_budget"
  },
  "links": {
    "commit": "/allocations/alloc_01J.../commit",
    "release": "/allocations/alloc_01J.../release"
  }
}</code></pre>
<p>The exact fields will vary, but the response should contain durable identifiers, state, time boundaries, the selected member or an opaque route reference, and links or actions for the next valid transitions. Do not return a permanent secret in an allocation object. If temporary credentials are required, deliver narrowly scoped credentials through a secure mechanism and make their expiration no longer than the lease.</p>
<h2 id="observability">Measure pool health from three perspectives</h2>
<p>Member health answers whether individual resources are alive and capable. Pool health answers whether the set can satisfy expected demand. Client experience answers whether allocations actually complete within the promised latency and reliability envelope. Monitoring only heartbeats misses overloaded members. Monitoring only average latency hides starvation for small tenants or rare capabilities. Track allocation success, queue time, lease expiry, utilization, error class, saturation, selection distribution, and reconciliation drift.</p>
<p>Use cardinality carefully. A metric labeled with every allocation identifier is expensive and difficult to aggregate. Keep high-cardinality identifiers in logs and traces, while metrics use bounded labels such as pool, region, capability class, result, and policy version. Define service objectives around user-visible outcomes: the percentage of eligible allocation requests fulfilled, time to allocation, and percentage of committed work reconciled correctly.</p>
<h2 id="security">Build tenancy and policy into the first version</h2>
<p>A pool is an attractive privilege-escalation boundary because it aggregates valuable resources. Require explicit tenant context, authorize both the pool and the requested capability, and validate that the selected member can be used for that tenant. Separate operator, contributor, allocator, consumer, auditor, and billing roles. Rate limits should apply at several layers so one API key cannot exhaust the entire pool while remaining under a single endpoint limit.</p>
<p>Secrets should be referenced, not copied into member metadata. Sensitive member fields should be encrypted and omitted from ordinary list responses. Log policy decisions and administrative changes, but redact prompts, personal data, wallet secrets, and customer payloads unless retention is necessary and disclosed. For external providers, record the provider and policy class used without exposing credentials or internal routing weights to untrusted clients.</p>
<h2 id="when-to-use">Know when a pool API is the right abstraction</h2>
<p>Use a pool when resources are substitutable enough that policy can select among them and when central coordination creates value. Typical benefits include higher utilization, failover, consistent quotas, simpler client integration, and consolidated observability. Do not pool resources that have materially different legal owners, settlement rules, data boundaries, or failure semantics unless the API makes those differences explicit. A single endpoint should not imply that every member is interchangeable.</p>
<h2 id="conclusion">The durable idea</h2>
<p>A strong pool API is a policy-controlled coordination layer. It gives clients a stable contract while allowing operators to change the members behind that contract. The design succeeds when the lifecycle is explicit, allocation is explainable, state transitions are safe under concurrency, and accounting can be reconstructed. Those principles apply whether the pool contains prompts, credits, GPUs, webhooks, nodes, stablecoins, or domain inventory.</p>
<p>Continue with the <a href="/api-portal/">Pool API portal blueprint</a> for a reference surface, or compare the specialized patterns in <a href="/pool-types/">Pool Types</a>.</p>
]]></content:encoded>
</item><item><title>Credit Pool API</title><link>https://poolapi.com/credit-pools/</link><guid isPermaLink="true">https://poolapi.com/credit-pools/</guid><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><description><![CDATA[Design pooled credits, quotas, points, prepaid usage, reservations, and immutable ledgers for APIs, AI models, and shared services.]]></description></item><item><title>Model &amp; Prompt Pool API</title><link>https://poolapi.com/model-pools/</link><guid isPermaLink="true">https://poolapi.com/model-pools/</guid><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><description><![CDATA[Route across LLM models, providers, prompts, and agent workflows with capability metadata, evaluations, budgets, versioning, and policy.]]></description></item><item><title>Compute Pool API</title><link>https://poolapi.com/compute-pools/</link><guid isPermaLink="true">https://poolapi.com/compute-pools/</guid><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><description><![CDATA[Pool GPU, CPU, RAM, VPS, container, and cloud capacity with resource shapes, leases, queue policy, isolation, scheduling, and observability.]]></description></item><item><title>Asset &amp; Token Pool API</title><link>https://poolapi.com/asset-pools/</link><guid isPermaLink="true">https://poolapi.com/asset-pools/</guid><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><description><![CDATA[Architecture for token, stablecoin, USDC, USDT, tokenized asset, and digital asset pools with ledgers, wallets, settlement, and reconciliation.]]></description></item><item><title>Node &amp; Endpoint Pool API</title><link>https://poolapi.com/node-pools/</link><guid isPermaLink="true">https://poolapi.com/node-pools/</guid><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><description><![CDATA[Manage nodes, workers, webhooks, endpoints, and service routes with enrollment, health, leases, draining, signatures, retries, and failover.]]></description></item>
</channel>
</rss>
