10 min read
How to Build a Product on a Label 309 Gateway
A Label 309 gateway is the publishing engine behind a Proof-of-Existence product: it owns Cardano submission, storage, balances, indexing, and webhooks, so your product can own the user experience instead.

To build a Proof-of-Existence feature without operating a blockchain yourself, you build on a gateway. A Label 309 gateway is the publishing engine behind a Proof-of-Existence product: it does the hard operational work — quotes, uploads, Cardano transaction submission, confirmation, reorg handling, record indexing, account balances, storage funding, API keys, and webhooks — and exposes all of it over plain HTTP. Your product calls those endpoints and owns everything your users actually see: the interface, the billing relationship, the domain workflow, and the brand.
The gateway core is open source, and CardanoWall is the reference product built on it. The useful part for developers is that there is no private door: CardanoWall reaches the gateway through the same public endpoints you would, so any pattern that works for the reference product works for you.
Who should build on a gateway?
Build on a gateway if you want Proof of Existence inside a product, not as a manual action a user performs on a website.
Good fits include:
- document notarization products;
- compliance evidence archives;
- AI provenance and dataset-governance platforms;
- continuous-integration proof systems;
- legal evidence tools;
- encrypted disclosure portals;
- media authenticity services;
- internal enterprise audit tools;
- publisher proof pages.
In all of these, users do not want to think about Cardano transaction construction, UTxO funding, storage credits, or chain confirmations. They want a workflow that produces durable, verifiable proofs. The gateway makes that workflow possible without your team becoming Cardano infrastructure operators.
What does the gateway own, and what does your product own?
The split is the whole point, so it is worth stating plainly. The gateway owns the base plane — the publish pipeline and all the money, chain, and storage state behind it:
- quote, upload, and publish;
- Cardano transaction build, submit, confirm, reorg handling, and refunds on permanent failure;
- content and ciphertext uploads to storage, plus the storage funding behind them;
- a shared on-chain records index covering every Label 309 record on the network;
- account balances and the ledger entries that move them;
- foreign-exchange pricing and margins;
- API credentials and webhook delivery.
This is infrastructure work. It has to be precise, observable, and boring in production. Your application should not reimplement it unless running a gateway is your actual job — and if it is, see run your own gateway.
Your product owns the vendor plane — everything vendor-shaped:
- user accounts and sessions;
- onboarding and billing;
- product permissions;
- your interface and your emails;
- the pricing you present to customers;
- domain concepts such as "case," "release," "dataset," or "evidence package";
- read models built from gateway events;
- support workflows.
The gateway does not need to know that a proof belongs to a lawsuit, a model-training run, a quarterly compliance package, or a magazine issue. It only needs to publish correct Label 309 records and maintain the operational state around them. That separation keeps both systems cleaner: your product can be rebuilt or rebranded without touching chain or money state, and the gateway can be upgraded without knowing your product exists.
How do you call the data plane on behalf of a user?
The data plane (/api/v1/*) is the account-facing API. Use it whenever the action happens on behalf of a user or account:
- quote a publish;
- upload content or ciphertext;
- publish a record;
- read a balance or the account ledger;
- list, fetch, or verify public records;
- register account-scoped webhooks.
In a wrapper product, your backend mints a short-lived account token for the user's gateway account, and the client calls the data plane with only the scopes it needs. The scopes are the permission boundary: a publish screen needs poe:create, a balance widget needs account:read, and the public records endpoints need no credential at all — they serve anonymous callers, which is what makes the index a public good rather than a per-customer view.
Tokens are short-lived by design (one hour by default). Mint one per session and re-mint on expiry, so a leaked token is a one-hour problem rather than an incident. And never send operator credentials to the browser — that is the one rule the next section exists to protect.
How do you call the control plane as the operator?
The control plane (/control/v1/*) is for trusted backends and operators only. Use it to:
- create gateway accounts, and enable or disable them;
- apply ledger adjustments after your billing system collects money;
- set per-account margins;
- mint account tokens and API keys;
- register the operator webhook firehose;
- register and manage wallets and storage funding sources;
- read audit and operational state.
This plane lives behind your backend. Treat operator credentials like production secrets with spending authority — because they have it. A short-lived operator token (24 hours by default) handles day-to-day administration; a single root secret, kept in a secret store, is reserved for the few operations that register wallets and storage sources.
The control plane is how your billing system and the gateway's prepaid balance stay connected. A payment succeeds in your system, and then your backend applies one idempotent ledger adjustment to the gateway account. That is the entire credit rail, and the next two sections explain why it is built exactly this way.
Why shouldn't you query the gateway's tables directly?
Because the schema is not a contract — the HTTP API and the webhooks are.
The gateway engine owns its own Postgres schemas (cw_core and cw_api). Your product should not read or write them, even when both systems share the same Postgres instance. Schema coexistence is a supported deployment shape, but the boundary is the schema, not the database: those engine schemas are internal and can change without notice, while the HTTP planes and the firehose are stable. If you reach across the boundary, a routine gateway upgrade can silently break your product.
If you need data the API does not expose, treat that as an API gap to raise — not a license to join across schemas.
How do you build your own screens from gateway events?
Every product needs its own views: sent records, customer history, balances, publish failures, case timelines, evidence packages, audit dashboards. Do not build those by polling every row or by joining into gateway internals.
Instead, register webhooks and project the events into your own tables. The gateway exposes an operator firehose — every lifecycle event on the instance — plus account-scoped webhook subscriptions for narrower workflows. A "sent items" view is the canonical example: consume the publish-status events, project them into your own table, and render from there.
Delivery is at-least-once, so make your projection idempotent. A practical read model can key on:
- the event id or delivery id (so a redelivery is a no-op);
- the gateway's record id;
- the final transaction hash;
- your own user id;
- your own domain object id;
- status and timestamps.
Your interface then renders from your own tables, while the gateway remains the authority for spend, the publish lifecycle, and chain facts. Each delivery is signed; verify the signature and enforce a timestamp tolerance window before you trust an event.
How should money flow between your billing and the gateway?
Keep the model simple: the gateway balance is the spendable balance, and your billing system is just one way money reaches it.
Your billing rail might collect cards, invoices, crypto payments, manual enterprise credits, or grants. The gateway does not need to know about any of that. The clean pattern is:
- Your billing system confirms a payment.
- Your backend applies one idempotent ledger adjustment to the gateway account, keyed on the payment's id.
- The gateway balance increases.
- Future publish and upload operations debit that balance.
- If a publish fails permanently, the gateway reverses the debit itself.
Two consequences follow. First, idempotency matters because billing pipelines deliver at-least-once: pass the payment's own id as the adjustment reference, and five deliveries of one payment credit the balance once. Second, do not mirror the gateway ledger into your own table and treat the mirror as truth for spending decisions — cache it for rendering if you must, but read the gateway whenever the decision actually moves money. Because the gateway issues its own refunds, you also should not build a vendor-side refund path for publish failures; it would double-refund.
Where should each kind of key live?
A gateway should never need a user's Identity Seed. The client or SDK can hash content, encrypt sealed payloads, and sign records locally; the gateway only publishes the finalized record bytes and submits the Cardano transaction. (For why that boundary matters end to end, see why keys never leave the device.)
A full integration has several distinct key classes, and the mistake to avoid is flattening them into one "API secret." They have very different blast radii:
- user Identity Seeds and recipient private keys — the user's cryptographic identity, which belongs at the edge;
- record-signing keys derived from that identity;
- account API keys and short-lived account tokens — which belong in the product or client context that needs them;
- operator tokens and the root secret — which belong only on trusted backends;
- the gateway's own Cardano and storage signing keys, and its webhook signing secrets — which belong in the gateway keyring.
Map each class to the smallest place that can hold it, and a single leak stays contained.
When should you self-host the gateway instead of using a hosted one?
Self-host when operational independence matters more than convenience.
A hosted gateway is the simplest path for most integrations: you publish through a ready service over standard API surfaces and never run chain or storage operations yourself. Self-hosting trades that simplicity for control — your own Cardano wallet funding, your own storage funding, your own operator policies, your own uptime and network dependencies, your own margins, your own compliance boundary, and no dependence on a third party for publishing.
But control is also responsibility. Self-hosting means you fund ADA and storage credits, protect the keyring, operate Postgres, monitor chain providers, handle backups, rotate credentials, manage webhooks, and respond to incidents. It removes a vendor dependency; it does not remove the operational work. Running your own gateway walks through what that actually entails.
What should your users see in a proof?
Most users should see your product concepts first. They care about "publish evidence," "seal this file," "prove this dataset snapshot," or "anchor this release." They should not have to read a metadata specification to use the product.
Even so, every proof view should expose the facts that make the claim independently checkable:
- the transaction hash;
- the block time and confirmation status;
- the hash algorithm and digest;
- the signer's public key, when the record is signed;
- storage URIs, when present;
- the sealed-recipient status, when relevant;
- the Merkle root and leaf proof, when the record batches many files;
- the verification verdict itself.
That is how the product stays pleasant to use without hiding the cryptographic claim underneath it.
How do you avoid locking your product to one gateway?
The gateway helps you publish; it should never become the proof.
The record on chain is Label 309 metadata, and verification is designed to work from three inputs only: the transaction metadata, optionally the content bytes, and a public Cardano explorer — with recipient keys added only to decrypt sealed records. No issuer server sits in that trust path. If your gateway disappears tomorrow, a valid record should still verify with independent tooling. Worth remembering, too, is what such a proof does and does not assert: it shows that exact bytes existed by a public time, not that any claim about them is true, owned, or authorized.
Build your product around that promise:
- store transaction hashes durably;
- let users export verification reports and download evidence packages;
- keep Merkle leaf lists and their proofs;
- document how to verify a record outside your interface;
- avoid private fields that only your backend can interpret.
That is the difference between "we have a database row" and "we produced a proof anyone can check."
Further reading
- What is a Label 309 gateway? and run your own gateway — the gateway from the operator's side.
- Build on the CardanoWall API and use the CLI in automation — the developer-facing surfaces in more detail.
- Why publishing has a price — what the prepaid balance actually pays for.
- Label 309 is open source — the standard and its reference code.
- The open-source standard lives at label309.org; the gateway, SDKs, and CLI are at github.com/cardanowall. Label 309 has been submitted to the Cardano CIP process and is under review by the CIP editors as a Metadata-category proposal.