Redirect (303) to the canonical privacy policy on bringyour.com. The api serves the redirect so a client that only knows the api host can still reach the policy.
URnetwork API: Other
URnetwork implementa por completo la especificación de operador del protocolo UR. Protocolo UR ↗
Other
62 operationsRedirect (303) to the canonical terms of service on bringyour.com.
Responses1 status · show schema
Redirect (303) to the vulnerability disclosure policy on bringyour.com.
Responses1 status · show schema
The serving status of the api process that answered, used by the deploy poll and by fleet status sampling. status is a latched value rather than a per-request health check, so this route stays O(1) and never couples a deploy poll to database load: "ok" while the process serves, a string beginning error while it is not ready (which fails a deploy poll), and "draining" from SIGTERM onwards (deliberately not an error, so an operator-initiated drain is not counted as a service error). host, service and block name the process that answered, and client_address is the caller address it observed.
Responses1 status · show schema
The process's build version; absent when it has none.
The deployed config version; absent when it has none.
The latched serving status: "ok", "draining" from SIGTERM onwards, or a string beginning "error" while the process is not ready (which is what fails a deploy poll).
The caller address this process observed.
Get the cumulative destination-reported bytes for contracts finalized on or after the start of subnet block 9. The counter advances when contracts reach a final outcome and is intended for public clients polling every 1-5 seconds.
Responses2 statuses · show schema
Base-10 destination-reported bytes for contracts finalized since since_block. This is a string so clients do not lose integer precision as the lifetime total grows.
First subnet block included in the counter.
UTC start time of since_block.
Upload a log file for the caller, associated with a feedback id. The request body is the raw log file content, streamed to storage. Note the {clientId} path segment is actually the feedback id the log is attached to.
udid. The feedback id the log file is associated with.
Request bodyshow schema
Responses1 status · show schema
Set, with a 200, when the upload was refused: over the per-network rate limit (one upload per network per period) or over the size limit.
The catalog an agent can buy from, so a skill can discover it without triggering a 402. Prices come from pro.yml, never from the x402 configuration, so an agent is quoted the same number a human pays by card; a sku with no price is not offered at all.
Responses2 statuses · show schema
True for the Pro-month sku, which grants the Pro entitlement. The data skus never do.
Buy a sku inline. Without an X-PAYMENT header the answer is 402 carrying the payment terms; the agent signs them and retries the SAME request with the signed payment in X-PAYMENT, which is settled and granted. The receipt is returned both as the body and in the X-PAYMENT-RESPONSE header. Requires a network session.
The same 402 round trip is also carried inline by POST /network/auth-client, where an agent meets the plan's concurrent-client limit.
The signed payment for terms previously quoted by a 402.
Request bodyshow schema
The network the agent intends to pay on. Must be one that was quoted. Optional when only one is configured.
Where to send the receipt. Optional.
Responses6 statuses · show schema
The Stripe webhook. Authenticated by the Stripe-Signature header over the raw body, verified before the event is parsed; there is no URnetwork JWT. The subscription is granted here, never by the client. Only the fields the server reads are listed; Stripe sends a much larger event.
Request bodyshow schema
The event's object, whose shape depends on type.
Responses1 status · show schema
Set only when the event was not acted on.
The Coinbase Commerce webhook, authenticated by the X-CC-Webhook-Signature HMAC over the raw body with the shared secret, checked before the event is parsed. Only the fields the server reads are listed.
Request bodyshow schema
Responses1 status · show schema
The Play RTDN push, delivered through Google Pub/Sub and authenticated as a Pub/Sub push -- the Authorization bearer is verified as a Google-signed OIDC token before the body is parsed. The notification itself is base64 inside message.data; the server decodes it and credits the renewal through the same advisory-lock gate as /subscription/verify-play-purchase and the payment reconciler.
Request bodyshow schema
base64 of the Play developer notification JSON.
Responses1 status · show schema
Present only when the notification was not acted on. The field is capitalized on the wire, because the Go field carries no json tag.
The Helius webhook for USDC transfers on Solana. Authenticated by the configured shared secret in the Authorization header, checked before the body is parsed. The server matches a transfer to a payment intent by the Solana Pay reference among the transaction's account keys, or by the memo text, and credits the plan or the data pack (see /solana/payment-intent and /pay/data/solana-intent).
The body is Helius's own enhanced-transaction ARRAY, not a wrapped object. One delivery carries a batch, and a customer's payment can sit behind any number of unrelated transfers, so a transaction that does not match is skipped rather than returned on. A per-transaction failure is reported only after the whole batch is examined, as a non-2xx, so Helius redelivers everything; the already-credited intents are no-ops on the retry.
Request bodyshow schema
Responses1 status · show schema
Set only when the transfer was not credited.
The Circle programmable-wallets notification, authenticated by the X-Circle-Signature ECDSA signature over the raw body under the key named by X-Circle-Key-Id. Deprecated; it is the same handler as POST /account/circle-wallet.
Request bodyshow schema
The first entry names the network the wallet belongs to.
Responses1 status · show schema
The App Store Server Notification V2 endpoint. The envelope carries one field, signedPayload, a compact JWS; unknown envelope fields are rejected and the body is capped at 1 MiB.
Every JWS -- the notification and the signed transaction and renewal info nested inside it -- is verified independently: ES256, a three-certificate x5c chain carrying Apple's leaf and intermediate policy extensions, terminating at a PINNED Apple root, with the chain judged at the payload's own signedDate. The outer notification must additionally be within 24 hours old and no more than 5 minutes in the future, and its bundle id, environment and app Apple id must match this app; the nested payloads must match the outer notification. A SUBSCRIBED, DID_RENEW or EXPIRED notification without signed transaction info is rejected.
Anything that fails is a 400 with no detail. A verified notification answers 200 with an empty object, whether or not it changed anything.
Request bodyshow schema
The compact JWS of the notification.
Responses3 statuses · show schema
Fetch the published Ed25519 public key for a client id. Unauthenticated by design - the value is a public key meant to be fetchable by any peer. A client that has never published a key returns {"public_key": null} with HTTP 200.
udid. The client id to look up.
Responses1 status · show schema
base64 encoded Ed25519 public key for the client id. null when the client has never published a key.
Fetch the operator-signed registration history of a client's public key: the ordered signed registrations, generation 1 first, each an opaque serialized registration exactly as the operator signed it. A peer verifies the chain itself -- every entry must be signed by a trusted signer under the deployment domain, generations must be contiguous and each entry must commit to the hash of the one before -- and compares the key it ends on with the one the platform attached to a contract, which is what turns the platform's key distribution from something a peer must trust into something it can check (connect/DESIGNNOTES3.md).
Unauthenticated by design, matching GET /key/{clientId}: every byte returned is already-published signed evidence. A client with no signed history -- a legacy client, or an operator that does not run the signed path -- answers {"history": []} with HTTP 200, never an error: an error is an availability signal, and a consumer must not confuse the two. At most 64 registrations, and at most 1 MiB, are returned.
udid. The client id to look up.
Responses1 status · show schema
The ordered signed registrations, generation 1 first, each the base64 of one opaque serialized registration. Empty when the client has no signed history.
Simple hello to the network that returns some useful information. This can be used for discovery.
It is also the extender network's trust root: extender_root_public_keys are the keys whose record signatures this network space accepts, and gossip_peer_id names the operator's gossip node. Both arrive over the platform's pinned tls, so a client can refresh them even when the request itself travelled through an untrusted extender -- and the extender activation probe forwards a real GET /hello through the candidate's tcp carrier to prove the forward works.
Responses1 status · show schema
The extender root keys whose record signatures this network space accepts, hex encoded. It arrives over the platform's pinned tls, so it is trustworthy even when the request itself travelled through an untrusted extender, and it REPLACES whatever list the client had stored. Absent when the operator has configured no extender network.
The libp2p peer id of the operator's gossip node, derived from its gossip identity key. A member dials the operator only when it knows this id, since without it there is nothing to authenticate the far end of the dial against -- so an operator running no gossip service serves none rather than an address a member could not verify.
Information about the caller's IP as seen by the platform: its geolocation, and whether the IP is currently connected to the network.
The location comes from MaxMind GeoLite2 City. This product includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com. The database has no privacy/VPN classification, so none is returned.
Responses1 status · show schema
city and region are left out when the IP is not located that precisely, e.g. only to its country.
The largest subdivision, e.g. a state.
ISO 3166-1 alpha-2, lowercase.
Two-letter continent code, lowercase.
IANA time zone, e.g. Europe/London.
When paying for a subscription with Stripe, create the payment intents, ephemeral key, and customer id needed to complete the purchase.
Request bodyshow schema
Responses1 status · show schema
Create a Stripe customer portal URL for the caller to manage their subscription.
Request bodyshow schema
Responses1 status · show schema
Buy a data pack with a card without signing in (the ur.io buy-data page). Starts a hosted Stripe checkout and returns the url to send the customer to. To pay with USDC on Solana use /pay/data/solana-intent.
With network_name, the data is applied to that network by the payment webhook as soon as the payment confirms, and the customer is emailed a note that the data is on the network (the code is included, already applied). The name is matched case-insensitively and exactly; an unknown name returns error.message "No network named ".
Without network_name, the customer receives a data code by email, as before. Stripe collects the email on its checkout page when email is not given.
The success url is the configured checkout success url plus item= and, when applying to a network, network=. Unauthenticated, per-ip rate limited. Errors are returned as error.message with a 200.
Request bodyshow schema
the hosted checkout; omitted or empty means stripe
the network that receives the data; omit to receive a code by email
where the code (or the applied note) is sent; Stripe collects it at checkout when omitted
Responses1 status · show schema
the hosted checkout url to send the customer to
the resolved network when network_name was given
Whether a network with exactly this name exists, for the buy-data page. Matched the same way /pay/data/checkout resolves network_name (case-insensitive, exact). Unlike /auth/network-check, which is a sign-up similarity check, a name within a few characters of an existing one is NOT reported as existing. Returns only the canonical name, no id. Unauthenticated, per-ip rate limited.
Request bodyshow schema
Responses1 status · show schema
the name as stored, when it exists
Buy a data pack with USDC on Solana, without signing in or giving an email: the data goes straight to the network named by network_name (matched like /pay/data/network-lookup). Records the payment intent and returns the exact amount_usd to send in USDC. The client generates reference (a Solana Pay reference: a fresh base58 public key) and either opens a Solana Pay url carrying it, or shows the buyer the receiving address, the amount and memo (which is the reference) to send by hand from any wallet or exchange. The Helius webhook (/pay/solana) matches the transfer by the reference among the transaction's account keys OR by the memo text, then lands the data on the network (data only, valid for one year, no Pro) and emails the network's admin when their login is an email.
The intent expires after 24 hours (expires_at); a payment that arrives late still credits as long as the intent has not been swept. A reused reference, an unknown item or an unknown network return error.message with a 200. Unauthenticated, per-ip rate limited.
Request bodyshow schema
the network that receives the data
the Solana Pay reference the client generated (a fresh base58 public key); it doubles as the transfer memo for a payment sent by hand
Responses1 status · show schema
the exact amount to send, in USDC
the transfer memo for a payment sent by hand (the reference)
the resolved network name, as stored
The state of a data pack payment started with /pay/data/solana-intent, for the buy-data page to poll while it waits: pending until the USDC transfer is credited, paid once the data is on the network, expired when the intent passed expires_at unpaid, unknown for a reference that is not a data pack intent (plan intents made by a signed-in wallet are not reported here). Unauthenticated, per-ip rate limited.
Request bodyshow schema
Responses1 status · show schema
Create a Stripe Checkout Session for a subscription or data pack.
ui_mode selects which shape the result carries -- a single Stripe session cannot produce both:
- "hosted" (the default, so existing callers are unchanged): returns
checkout_url, which the client opens in a browser. - "embedded": returnsclient_secretandpublishable_key, which the client uses to mount Stripe Embedded Checkout inline (the desktop apps load ur.io/checkout in a webview with these).
The subscription is granted by the invoice.paid webhook, never by the client; the client only polls /subscription/balance afterwards.
Request bodyshow schema
ISO 3166-1 storefront country, when the caller knows it. The Pro items are priced at the caller's regional tier and, when the welcome offer is redeemable (yearly only), the 25% coupon is applied server-side; the charged tier is finalized from the card's billing country once attached.
"hosted" (default) or "embedded".
"never" keeps an EMBEDDED checkout fully inline: Stripe fires the client's onComplete callback instead of redirecting, so the page the customer is on never navigates. Only valid with ui_mode "embedded". Empty means the embedded flow redirects to the configured return url, and hosted behaves as always.
Responses1 status · show schema
which of the two shapes below is populated
hosted mode only
embedded mode only
embedded mode only
both modes; the caller can reconcile the purchase with this
One step of the validator's routing-verification protocol. The route accepts two body shapes:
- SEED starts a new trail through the validator-chosen entry provider.
- EXTEND claims the pending assigned hop of an active trail.
The request must egress from the provider being claimed - the server resolves the hop from the request source IP, never from the body. Authentication is the protocol's own Ed25519 signatures (over the canonical binary messages, not the JSON), not a JWT. A non-final step returns the assign result (the server's signed commitment to the next hop); the final step returns status "complete" and the published proof.
Request bodyshow schema
uuid. The validator's own client_id; the server checks vpk equals this client's registered Ed25519 key.
base64 encoded 32-byte Ed25519 validator path key.
base64 encoded 32-byte nonce.
base64 encoded 64-byte Ed25519 SEED signature.
Requested trail depth. The server clamps to its allowed range.
uuid. The validator's own client_id.
uuid
The ordered confirmed hop client_ids plus the single pending hop being claimed.
uuid
base64 encoded 64-byte Ed25519 EXTEND signature.
Responses2 statuses · show schema
uuid
base64 encoded 32-byte per-trail server nonce.
The ordered confirmed hop client_ids (ids only; times are published only in the final proof).
uuid
uuid. The newly assigned pending hop.
The server-clamped effective trail depth.
1-byte id of the server key that signed assign_sig. See /verify/keys.
base64 encoded 64-byte Ed25519 ASSIGN signature by the server (msg_type 0x03).
"complete"
uuid
base64 encoded 32-byte per-trail server nonce.
base64 encoded 32-byte Ed25519 validator path key.
Trail depth.
uuid. The canonical provider at this hop.
Server-stamped confirmation time, unix milliseconds UTC.
The 32-byte hash of the provider's egress IP at the subnet's configured prefix granularity. Marshals on the wire as a JSON array of 32 integers (a Go [32]byte), not a base64 string.
1-byte id of the server key that signed final_sig.
The server-attested coverage of the trail (v1 = M-1, the server-assigned confirmed hops with the seed excluded).
base64 encoded 64-byte Ed25519 FINAL signature by the server.
base64 encoded 64-byte Ed25519 depth-M EXTEND signature by the validator.
The published server Ed25519 verify keys, by serverkeyid. All historical keys are listed so old proofs remain verifiable across key rotations.
Responses2 statuses · show schema
1-byte key rotation id.
base64 encoded 32-byte Ed25519 public key.
The public reproducibility index of the routing-verification protocol: per provider and period, how many hops were assigned and how many were confirmed, the resulting reliability in parts per million, and the latency percentiles when they were measured. The answer names the profile and policy hash the rows were produced under, plus the egress hash key id (the id only -- the key itself never leaves the server), so a third party can check that two indexes are comparable.
Unauthenticated, and it does not participate in validator scoring. 503 while the subnet subsystem is disabled.
RFC 3339 start of the window.
RFC 3339 end of the window.
Responses3 statuses · show schema
The id only; the key itself never leaves the server.
The published completed trails, so anyone can re-verify them against the keys from /verify/keys. Same window parameters, profile and policy hash as /verify/stats. Unauthenticated; 503 while the subnet subsystem is disabled.
RFC 3339 start of the window.
RFC 3339 end of the window.
Responses3 statuses · show schema
The coldkeys attached inside the caller's network: the network-level wallet (no client_id) and every provider client's wallet. wallet is the session's effective wallet (the client-scoped wallet for a client session when set, else the network wallet); absent when none is attached. An empty wallets list is not an error.
Responses1 status · show schema
uuid of the provider client; absent for the network-level wallet.
Unix time in milliseconds the wallet was set.
uuid of the provider client; absent for the network-level wallet.
Unix time in milliseconds the wallet was set.
Attach the caller's subnet coldkey - the ss58 address that pool payouts are claimable by. Proof of key possession is required: message is the single-use challenge issued by POST /auth/wallet-challenge for blockchain TAO and signature is the coldkey's sr25519 signature over it. client_id scopes the wallet to one provider client (a client-scoped JWT selects its own client). Banned addresses are rejected. Not retroactive: the wallet applies from the next committed epoch's leaf set. This wallet is deliberately separate from the account payout wallet.
Request bodyshow schema
The subnet coldkey as an ss58 address (Bittensor prefix 42).
uuid of the provider client the wallet is attached to. Defaults to the session client for a client-scoped JWT; must belong to the caller's network.
The coldkey's sr25519 signature (hex) over message. Required for app callers.
The exact single-use challenge text issued by POST /auth/wallet-challenge for blockchain TAO and this address.
Responses1 status · show schema
Checks an address before it is attached: ss58 syntax with the Bittensor prefix, the operator ban list, and whether the account exists on the subtensor chain. When the chain cannot be reached the result reports exists_on_chain: true with a message, so an outage never blocks a user. No authentication; chain lookups are rate limited per source address.
Request bodyshow schema
The ss58 address to check.
Responses1 status · show schema
The address decodes as ss58 with the Bittensor prefix.
The account exists on the subtensor chain (true when the chain could not be reached; see message).
The address is on the operator ban list and must not be attached.
The caller network's standing for a head mining spot (Top 200, WHITEPAPER 8.4). score is the server's estimate of split-adjusted distinct routable egress-IP breadth from the live trail egress index, floor the score of the last network inside cutoff (0 while fewer networks score), rank_estimate 1-based (0 without a score). bound/hotkey/uid reflect an active head binding. source is "server" until validators publish consensus scores on chain; a chain outage degrades to the estimate, never an error.
Responses1 status · show schema
The estimate places the network inside the cutoff.
Split-adjusted distinct routable egress-IP breadth (server estimate).
Score of the last network inside the cutoff; 0 while fewer networks score.
1-based estimated rank; 0 without a score.
Head-tier size (200).
An active head binding exists for one of the network's clients.
The bound head hotkey as ss58; absent when not bound.
The bound uid; 0 when not bound.
The bound network's estimated rank; 0 when not bound.
The current contract epoch.
"server" (estimate) or "chain" (validator consensus).
Store a device's consent for a fleet binding (WHITEPAPER 11.4): the canonical binding payload and the client's Ed25519 signature over its keccak256 digest, optionally with the head hotkey's sr25519 signature. When both signatures verify the response carries the bindFleetMember calldata for the operator to submit from their own key. The server never submits on chain.
Request bodyshow schema
0x-hex coordinator contract address.
uuid
0x-hex 32-byte client Ed25519 public key.
The device's Ed25519 signature (hex) over the binding digest; signature is accepted as an alias.
The head hotkey's sr25519 signature (hex) over the same digest; optional, required for calldata.
Optional override of the binding's hotkey (ss58 or 0x-hex).
Responses1 status · show schema
0x-hex keccak256 binding digest.
Both signatures verified and calldata is present.
0x-hex bindFleetMember calldata for the operator to submit.
The coordinator contract address to send the calldata to.
Same as calldata.
The caller network's merkle pool-payout claim for an epoch: everything needed to call claim on the subnet contract. Claims are served once the epoch is finalized on chain.
Epoch index. Absent means the latest finalized epoch -- and only absence defaults, since epoch 0 is a real epoch.
Responses1 status · show schema
Epoch index.
base64 encoded 32-byte network operator id, as committed on chain.
base64 encoded 32-byte coldkey public key (the ss58-decoded key set via /sn/wallet).
The caller's payout share in basis points.
Merkle proof from the (noid, coldkey, sharebps) leaf to payout_root.
base64 encoded 32-byte committed payout merkle root.
0x-hex EVM address of the subnet contract.
EVM chain id of the subnet contract chain.
Block number claims open at.
The content hash of the payout artifact this claim was computed from, when one is recorded. Fetch it from /sn/artifact.
Where that artifact is published, when recorded.
0x-hex EVM address of the settlement vault the claim is sent to; absent when the operator has not configured one.
The current subnet epoch index, boundaries, and deadlines mirrored from chain, so clients do not need their own RPC.
Responses1 status · show schema
Epoch index.
Epoch length in blocks.
EVM chain id of the subnet contract chain.
0x-hex EVM address of the subnet contract.
0x-hex EVM address of the settlement vault claims are sent to; absent when unset.
The operator id (decimal string); absent when unset.
Public subtensor EVM JSON-RPC url for clients; absent when unset.
Serve a canonical payout artifact addressed by its sha256 content hash. The hash is rechecked against the bytes before anything is returned, so a corrupt object store cannot silently substitute a different payout tree. Public and immutable: the answer carries the content hash as its ETag and a one-year immutable cache directive.
The artifact's sha256 content hash.
Responses4 statuses · show schema
Always urnetwork-payout-artifact-v1.
0x-prefixed hex digest.
0x-prefixed 20-byte EVM address.
0x-prefixed 20-byte EVM address.
0x-prefixed hex digest.
0x-prefixed block hash.
0x-prefixed block hash.
sha256:-prefixed hex digest.
sha256:-prefixed hex digest.
sha256:-prefixed hex digest.
The provider inputs the tree was built from; null when the epoch had no providers (a nil slice in the builder).
The 16-byte client id, as a JSON array of byte values.
The 32-byte coldkey public key, as a JSON array of byte values.
Sibling hashes from the leaf to the root, each a 32-byte value as a JSON array of byte values; null for a tree that needs none.
The 32-byte merkle root, as a JSON array of byte values.
RFC 3339 UTC.
0x-prefixed 20-byte EVM address of the signing key.
0x-prefixed hex of the 65-byte secp256k1 signature over content_hash.
List immutable payout artifact keys under a deployment and netuid prefix, optionally narrowed to one epoch and one operator id. Only public object identifiers are returned -- never signer or vault material. The page is validated as an integrity boundary before it is served: keys must be strictly increasing, inside the requested prefix, and named by a canonical lowercase 32-byte hex hash with a .json suffix.
One safe path segment (no /, \ or .).
A nonzero uint16.
A uint64. Required if no_id is given.
A nonzero uint64 operator id; only valid with epoch.
Objects per page, default 256.
The previous page's next_after; must be inside the same prefix.
Responses4 statuses · show schema
Always urnetwork-payout-artifact-history-v1.
sha256: followed by the lowercase 64-character hex hash.
The cursor for the next page; empty on the last page.
Stream the exact immutable bytes of one attempt object. kind selects the shape: metadata is a JSON document, records and proofs are newline-delimited JSON. hash is the canonical lowercase, nonzero, 0x-prefixed sha256 of those bytes and is what the answer's ETag carries. Exactly these two query parameters are accepted, and a Range header is refused, so a partial read can never be mistaken for the whole object.
Concurrency is bounded (16 readers, each with one fixed 32 KiB copy buffer) and a request that finds no slot is answered 429 with Retry-After. A stream that fails after any bytes have been written is aborted without a clean end-of-body rather than ending in a truncated object that looks complete.
Canonical 0x-prefixed lowercase sha256 of the object.
Responses6 statuses · show schema
0x-prefixed sha256 of the first descriptor page; zero for an empty stream.
0x-prefixed sha256 of the chunk bytes.
0x-prefixed sha256 of the next page; zero on the last page.
Upload the exact bytes of one attempt object. Requires a CLIENT jwt. The identity is the same kind and hash pair the reader uses, and the server recomputes the sha256 of the body and refuses anything that differs, so an upload can only ever publish the object it names.
The framing is strict: exactly one Content-Type matching the kind (application/json for metadata, application/x-ndjson for records and proofs), a positive Content-Length within the object bound (32 MiB for records and proofs, 2 MiB for metadata), and no Content-Encoding, Content-Range, Range, transfer encoding or trailers -- a transformed or partial upload is forbidden. Size is charged against the account's byte budget BEFORE any storage work, and the active upload slots are bounded.
A validator may instead present a signed reserved-upload intent in X-Urnetwork-Validator-Upload, which admits the upload against the api's own reserved lifecycle. The reserved header never bypasses client authentication.
The answer is 204 with the content hash in ETag.
Canonical 0x-prefixed lowercase sha256 of the body.
A signed validator reserved-upload intent.
Request bodyshow schema
Responses9 statuses · show schema
Serve a signed release-evidence envelope by its content hash. The envelope's signature is re-verified and its content hash re-checked against the requested one before the bytes are returned. Public and immutable, with the content hash as ETag.
Responses5 statuses · show schema
0x-hex.
The evidence payload; its shape depends on kind.
0x-hex EVM address of the signing operator artifact key.
sha256: followed by the lowercase hex hash of the canonical bytes.
Publish a release-evidence envelope. There is no header credential: the authorization IS the envelope's own signature, which must verify and must be the operator's configured artifact key, and whose chain id, netuid, deployment id and genesis hash must all match this operator. The body is capped at 64 MiB. Every rejection is the same 400, so a caller cannot probe which check failed.
Request bodyshow schema
0x-hex.
The evidence payload; its shape depends on kind.
0x-hex EVM address of the signing operator artifact key.
sha256: followed by the lowercase hex hash of the canonical bytes.
Responses2 statuses · show schema
The immutable object key the envelope was published at.
The key under the run's history prefix.
List the evidence objects of ONE signed run. Both kind and run_id are mandatory, so a public caller cannot enumerate other campaigns or turn a small verifier query into an unbounded deployment scan.
With hash the answer is that one object, fully re-verified (signature, deployment, netuid, kind, run and content hash) rather than merely listed, and after may not be combined with it. The legacy empty-run namespace is readable only by exact hash.
A nonzero uint16.
Read exactly this object instead of paging.
Objects per page, default 256.
The previous page's next_after. Not valid with hash.
Responses5 statuses · show schema
Always urnetwork-release-evidence-history-v1.
The cursor for the next page; empty on the last page.
Publish one signed client-key observation and read back that client's key history. A key observation creates a durable signed public statement, so it is an explicit POST owned by an authenticated CLIENT session rather than a side effect of the public GET /key/{clientId} lookup, and the request cannot supply a key to sign.
The framing is strict: Content-Type: application/json, a positive Content-Length of at most 16 KiB, no content encoding, transfer encoding or trailers. The body must be exactly the canonical encoding of the request -- unknown fields, trailing JSON and any non-canonical re-encoding are refused -- and the full maximum response size is charged against the account's byte budget before any operator signature or public write, so a failed call still pays its admission.
Request bodyshow schema
uuid. The client whose key history is being observed.
base64 of the encoded signed observation request, at most 8 KiB. The request cannot supply a key to sign.
Responses4 statuses · show schema
The client's signed key history statements, base64 encoded, oldest first.
base64 of the signed observation this call published.
The plural form of /sn/client-key/observation, with the same authenticated, bounded and charged publication contract: one HTTP body never becomes one logical quota item, so the maximum response size is charged once PER request in the batch before any work is done. The body is capped at 1 MiB and the responses come back in request order, each an encoded client-key history response.
A batch whose chain work cannot be admitted is refused with 413 and an X-Ur-Client-Key-Batch-Admission: work header.
Request bodyshow schema
A 16-byte client id, on the wire as a JSON array of 16 integers.
A 32-byte hotkey, on the wire as a JSON array of 32 integers.
A 32-byte hash, on the wire as a JSON array of 32 integers.
A 32-byte hash, on the wire as a JSON array of 32 integers (a Go [32]byte).
A 32-byte nonce, on the wire as a JSON array of 32 integers.
Responses7 statuses · show schema
One encoded ClientKeyHistoryResponse per request, in request order.
The client's signed key history statements, base64 encoded, oldest first.
base64 of the signed observation this call published.
Issue the caller's welcome offer: 25% off the first year of Pro (3 months free), valid validity_days (5) from issue, stacked on the 14-day trial, redeemable on every store. Issued ONCE per network: the call is idempotent and returns the existing offer in whatever state it is in (created: false); an expired offer is never re-issued. Call it the moment the offer surface renders (the intro plan step), so the deadline is real.
Refused with error when the onboarding config is absent, or when the caller is in the holdout variant of the in-app offer experiment (experiments["offer.in_app"]): those networks show the regular plan picker and reach the offer by email instead.
Budgeted per caller address (30 per 10 minutes): over budget the response is 429 with a Retry-After header.
Request bodyshow schema
Where the offer is being shown. Only these three are accepted here; empty means intro_step, and anything else (including email_link, which only the campaign engine issues) is refused with error.message "Unknown surface.".
ISO 3166-1 storefront country, when the app knows it
Responses3 statuses · show schema
The offer. The key is ABSENT (not null) when refused.
When the offer was first redeemed; null until then.
The store of the first redemption; null until then.
true for the call that issued the offer
The key is ABSENT (not null) on success.
Store a batch of product events (at most 200 per call) against the CLOSED event schema (see ClientEvent). The network id comes from the bearer token only; tier, path, experiment and variant are stamped by the server. platform is required and must be one of the known platforms: an event without one is rejected. Unknown event names, unknown prop keys, missing required props and ill-typed values are refused PER EVENT in rejected (the rest of the batch is stored); a rejection is final, the client must not resend that event. No free text is stored except feedback.submitted.text, which is redacted (emails, phone numbers, IP addresses) before storage.
Clients batch every 30 s or on background, drop an event after three failed calls, and never send more than 200 per call (a larger batch is a 400). Budgeted per caller address (120 calls per 10 minutes): over budget the response is 429 with a Retry-After header.
Request bodyshow schema
free text, redacted (emails, phone numbers, IP addresses) before storage
Responses4 statuses · show schema
events stored (or deduplicated)
schema refusals by batch index; final, do not resend
Record a campaign landing-page click. Every onboarding email link is https://ur.io/o/?t=; the landing page posts the token here, which writes the server-side landing.clicked attribution event and answers with the in-app destination to route to (onboarding/connect, onboarding/widgets, onboarding/offer, onboarding/feedback). An app open (/network/auth-client) within 48 h of the click is attributed to it as app.opened.
No authentication: the signed token is the credential, and the response carries nothing about the network. An expired token still returns step and destination (with ok: false, error: expired) so the page can route without attributing; an invalid token returns ok: false, error: invalid. Budgeted per caller address (60 per 10 minutes) -> 429 with Retry-After.
Request bodyshow schema
Responses2 statuses · show schema
the campaign step the link belongs to (e.g. e1_connect)
the in-app destination to route to
Resolve a feedback link token (https://ur.io/f/?r=n or ?why=n) to the rating or reason the one-tap email button stood for, so the in-app feedback screen opens pre-filled. The token carries the step and may carry the rating/reason; r (1-5) and why (a reason token) override when present. The app then submits the actual feedback through /feedback/send-feedback and reports feedback.submitted through /client/events.
No authentication: the signed token is the credential. Budgeted per caller address (60 per 10 minutes) -> 429 with Retry-After.
The signed feedback token from the email link.
Rating 1-5 from the tapped button.
Reason token from the tapped button.
Responses2 statuses · show schema
1-5. The key is ABSENT when the link carried no rating (the server omits the zero value rather than sending 0).
The nightly aggregate the analysis script (mmm/onboarding/analyze.mjs) reads: onboarding_results_daily, recomputed at 02:00 UTC by the OnboardingResultsRollup task for the last onboarding.results.rollup_days cohort days (config/main/onboarding.yml). One row per cohort day (the network's sign-up day, UTC), experiment, variant, surface, platform, price tier and path. Counts are networks, never events.
Exposure is the registry assignment (hash of network id and experiment id), so holdout rows exist without any stamped event; for the email sequence it is the variant the campaign row was served, and only networks with an email address are exposed to an email experiment. The pseudo-experiment _all (variant all, surface all) counts every network of the cohort so the funnel per platform, tier and path is readable without an experiment. The cohort is the campaign's real population: a network enters (its cohort time is the enrollment time) when its login is an email address, otherwise on its first device client within 30 days of creation; a network with neither an email login nor a device is never a cohort member.
Column definitions, measured from each network's cohort time: sent, delivered, opened, clicked, unsubscribe, complaint = at least one email.* event of that name, any step; landing_clicked = a landing.clicked; app_open_48h = an app.opened (the auth-client attribution within 48 h of a landing click); connect_7d = a connect.day (server-written on each UTC day the network had a connection) or a connect.first within 7 days; widget_7d = widget.added within 7 days; feedback_7d = feedback.submitted or an account feedback within 7 days; pro_start_14d = a purchase.completed or the first subscription renewal (trial included) within 14 days; trial_to_paid_35d = trial.converted within 35 days; refund_60d = a refund within 60 days; retention_d7 = a connect.day on day 6 or 7; retention_d30 = a connect.day on day 28, 29 or 30. Connection days are the analytics events, never the connection table (which is pruned hours after disconnect), so they exist from the day the server started writing connect.day. A window that has not matured reports 0: read matured_days (the cohort's age at computed_at) to tell "not yet" from "did not".
Volume floor: rows with fewer than onboarding.results.min_exposures exposures (default 10) are suppressed, and no identifier of any kind is returned. Ordered by the dimension tuple; keyset paged by cursor.
Auth: Authorization: Bearer where the token is listed in vault onboarding.yml under onboarding.admin_bearers (compared in constant time). A missing or unknown token is 401; a network JWT is
- Without the vault entry the endpoint is closed.
The experiment id from the registry.
First cohort day, inclusive (YYYY-MM-DD).
Last cohort day, inclusive (YYYY-MM-DD).
The campaign path, A (saw the in-app offer) or B.
The next_cursor of the previous page.
Rows per page, 1-1000 (default 500).
Responses4 statuses · show schema
An EventPlatform, or unknown when the sign-up did not say.
A PriceTierName, or unknown when the sign-up country is unknown.
The campaign path A or B, or unknown.
The cohort's age in whole days at computed_at; a window longer than this has not been measured yet.
The cursor for the next page; null on the last page.
The volume floor applied to this page.
The durable per-flow-step send and engagement aggregate of the onboarding email campaign, one row per send day and dimension tuple (step, template, variant, experiment, experiment variant, platform, path). Counts are DISTINCT NETWORKS, never events, and no network, address, message or session identifier is ever returned; rows below the configured minimum-exposure floor (min_networks) are suppressed.
engaged is the union of email.clicked, landing.clicked, app.opened, connected, widget.added, feedback.submitted and pro.started, deliberately excluding email.opened (open tracking is too unreliable to count as engagement). attribution_ambiguous counts events that could only be attributed to a template shared by more than one flow step. The definitions block returns these rules with the data, so a reader never has to assume them.
Same auth as /admin/onboarding/results: an admin bearer from vault onboarding.yml, compared in constant time. Keyset paged by cursor.
First send day, inclusive (YYYY-MM-DD).
Last send day, inclusive (YYYY-MM-DD). Must not precede from, and the range is bounded.
A flow step; e1, e2, e3, e4 or e5.
The campaign path, A or B.
The next_cursor of the previous page.
Responses4 statuses · show schema
The union of the engagement events named in definitions.engaged_includes.
Events attributable only to a template shared by more than one flow step.
The cursor for the next page; null on the last page.
The volume floor applied to this page.
The onboarding experiment registry (config/main/onboarding.yml onboarding.experiments) as the server loaded it, so the analysis script judges results against the same allocation, primary metric, guardrails and minimum exposures the server applied, plus each experiment's live variant states from the pause overlay (network_onboarding_experiment_state: a variant the nightly guardrail check or bringyourctl onboarding experiments --pause paused is served as control until resumed) and the results configuration (min_exposures, rollup_days, guardrail_days). Same auth as /admin/onboarding/results.
Responses3 statuses · show schema
email. | offer.in_app | offer.email | cadence
percent per variant name, by hash(network_id, id)
per variant, the Brevo template name or the store copy key set and layout id
Whether the experiment assigns right now (status running, within start/stop).
The pause overlay rows of this experiment (empty when nothing was ever paused).
paused = served as control; running = resumed.
The results configuration the rollup and the results endpoint apply.
The Brevo transactional-email webhook. Brevo is registered (idempotently, by the server when onboarding.enabled is on) to post one event per delivery step.
Authentication is HTTP BASIC, not bearer and not a network token: the request must carry Authorization: Basic base64(:) where is one of the account's webhook_bearers (vault brevo.yml) and the password half is empty.
unsubscribe turns the account's product-updates preference off (the existing behavior). For the onboarding campaign the remaining events are recorded as server-written email.delivered|opened|clicked|bounced|complained|unsubscribed events against the network, with the campaign step taken from the stored send that message-id names. An event whose message id is not a known campaign send records nothing.
The event name is matched lower-cased against this exact set: delivered; opened, unique_opened, proxy_open; click, clicked; hard_bounce, blocked, invalid_email, error; spam, complaint; unsubscribed, unsubscribe. Anything else records no campaign event. A hard bounce or a complaint exits the network from the sequence.
Request bodyshow schema
the send's tags; the campaign sends carry [onboarding, , ]
Brevo's legacy form of the tags, a JSON-encoded array in a string
The campaign sets this header to the step name on every send. The webhook path does not read it back; the step comes from the stored send that message-id names.
the clicked URL (click events)
unix seconds of the event
Responses2 statuses · show schema
The rfc 8414 authorization server metadata. Served with Cache-Control: no-store, and identical to the OpenID Connect discovery document below. The issuer it publishes is the hostname clients must use, which is not necessarily the api host this request reached.
Responses1 status · show schema
rfc 8707.
The OpenID Connect discovery document. The same document as /.well-known/oauth-authorization-server.
Responses1 status · show schema
rfc 8707.
The authorization server's public signing keys as a JWK set. All current keys are listed so tokens stay verifiable across a rotation.
Responses1 status · show schema
The rfc 6749 token endpoint, as a form post. Two grants are supported: authorization_code (with the PKCE code_verifier, and optionally the rfc 8707 resource indicator) and refresh_token. Clients are public -- the token endpoint auth method is none -- so client_id is always required and there is no client secret.
Errors are the rfc 6749 section 5.2 envelope. The descriptions are deliberately terse and the detail is logged rather than returned, so an error cannot be used to probe which check failed. The response is never cached by a shared cache.
Request bodyshow schema
authorization_code grant.
authorization_code grant; must be registered for this client.
authorization_code grant; the PKCE verifier.
authorization_code grant; the rfc 8707 resource indicator.
refresh_token grant.
refresh_token grant; a space-separated subset of the granted scopes.
Responses4 statuses · show schema
Dynamic client registration (rfc 7591). Deprecated by the MCP specification in favor of client id metadata documents, and kept for clients that predate it. The endpoint is openly writable, so it is metered per caller address before the body is parsed or anything is stored; over budget it answers 429 with Retry-After. Registered clients are public, so no secret is issued.
Request bodyshow schema
Responses3 statuses · show schema
Always none -- registered clients are public and have no secret.
Token revocation (rfc 7009), as a form post. Always answers 200 with an empty body: per the rfc a client must not be able to tell an unknown token from a revoked one.
Request bodyshow schema
Responses2 statuses · show schema
The OpenID Connect userinfo endpoint. Authenticated by an ACCESS TOKEN this authorization server issued, carrying the openid scope -- not by a URnetwork network JWT. The token's audience is the resource it was minted for rather than this issuer, so the audience is deliberately not constrained here.
Responses3 statuses · show schema
What the consent page on the ur.io origin needs to render: who is asking, for what scopes, and whether consent is actually required. The caller is the consent page passing through the signed-in user's URnetwork JWT.
The authorization request is validated here, BEFORE anything is rendered, so a hostile request never reaches a screen that could be used to phish approval; an invalid request is reported to the user rather than redirected, because a redirect uri is only trustworthy once it has matched the client's registered set. prompt=consent forces the screen even when the scopes are already approved. issuer is returned so the page can echo iss when the user declines, as rfc 9207 requires on error responses too.
Request bodyshow schema
Defaults to code.
Defaults to S256.
rfc 8707 resource indicator.
consent forces the screen even when the scopes are already approved.
Responses3 statuses · show schema
The requested scopes, filtered to the supported set.
Echoed as iss when the user declines, per rfc 9207.
Mint the authorization code for a request the user approved, and return the redirect the consent page should follow. The caller is the ur.io consent page passing through the signed-in user's URnetwork JWT. Consent is recorded HERE rather than when the screen is shown, so an abandoned screen grants nothing. The redirect carries code, the request's state when it had one, and iss per rfc 9207 so the client can detect a mix-up attack.
Request bodyshow schema
Defaults to code.
Defaults to S256.
rfc 8707 resource indicator.
consent forces the screen even when the scopes are already approved.
Responses3 statuses · show schema
Where the consent page should send the user agent; carries code, iss and any state.