Create an expiring, server-priced quote. No charge is made.
/quotesCreate an expiring, server-priced quote. No charge is made.
Every contract sourcedEvery check datedGaps shown, never filledBuild with URSY
Direct machine-to-machine commerce. Reading the catalogue and taking a quote are open to any agent: a quote is server-priced, GBP excluding VAT, valid 15 minutes and never a charge. Ordering is not open: POST /orders validates the request, then refuses with ordering_closed and takes no payment. Every error uses the ErrorEnvelope shape.
https://ursy.net/wp-json/ursy-net/v1Endpoints are extracted from this version. Original snapshots are retained when a source changes.
Requirements are recorded per operation. An API can contain both public and protected endpoints.
Read the authentication guide →/quotesCreate an expiring, server-priced quote. No charge is made.
/ordersPlace an order against an unexpired quote. Validates fully, then refuses while ordering is closed. No charge is ever made by this route.
/mandatesRead bounded delegated spending authority. Requires an owner grant.
/catalogueRead every offer, transcribed from its owning property. Nothing is purchasable.
/quotes/{quote_id}Read one issued quote and whether it has expired.
/orders/{order_id}Read one placed order. Requires an owner grant.
/offers/{offer_id}Resolve one offer by its ursy:: identifier.
{
"openapi": "3.1.0",
"info": {
"title": "URSY.net API",
"version": "0.3.2",
"description": "Direct machine-to-machine commerce. Reading the catalogue and taking a quote are open to any agent: a quote is server-priced, GBP excluding VAT, valid 15 minutes and never a charge. Ordering is not open: POST /orders validates the request, then refuses with ordering_closed and takes no payment. Every error uses the ErrorEnvelope shape.",
"termsOfService": "https://ursy.net/terms/",
"contact": {
"name": "URSY",
"url": "https://ursy.net/contact/",
"email": "[email protected]"
}
},
"servers": [
{
"url": "https://ursy.net/wp-json/ursy-net/v1"
}
],
"paths": {
"/status": {
"get": {
"operationId": "ursyNetStatus",
"summary": "Read measured service and release status.",
"tags": [
"public"
],
"security": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/capabilities": {
"get": {
"operationId": "ursyNetCapabilities",
"summary": "Read implemented and enabled capabilities.",
"tags": [
"public"
],
"security": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/catalogue": {
"get": {
"operationId": "ursyNetCatalogue",
"summary": "Read every offer, transcribed from its owning property. Nothing is purchasable.",
"tags": [
"public"
],
"security": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/offers/{offer_id}": {
"get": {
"operationId": "ursyNetOffers",
"summary": "Resolve one offer by its ursy:<property>:<sku> identifier.",
"tags": [
"public"
],
"security": [],
"parameters": [
{
"name": "offer_id",
"in": "path",
"required": true,
"description": "e.g. ursy:email:email-solo",
"schema": {
"type": "string",
"pattern": "^[A-Za-z0-9:._-]+$"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"404": {
"description": "Not found (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/mcp": {
"post": {
"operationId": "ursyNetMcp",
"summary": "Product MCP endpoint (JSON-RPC 2.0 over POST).",
"tags": [
"mcp"
],
"security": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/JsonRpcRequest"
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/JsonRpcResponse"
}
}
}
},
"202": {
"description": "Notification accepted (no body)"
},
"400": {
"description": "Malformed or invalid request (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/mandates": {
"get": {
"operationId": "ursyNetMandates",
"summary": "Read bounded delegated spending authority. Requires an owner grant.",
"tags": [
"owner"
],
"security": [
{
"bearerAuth": [
"net.mandates.read"
]
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"401": {
"description": "No valid URSY.ID access token for this resource (typed: unauthorized, invalid_token or identity_not_configured)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"403": {
"description": "Refused: ordering_closed, or a token without the required scope",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"503": {
"description": "The authorization server could not be reached",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/quotes": {
"post": {
"operationId": "ursyNetQuotes",
"summary": "Create an expiring, server-priced quote. No charge is made.",
"tags": [
"public"
],
"security": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"offer_id": {
"type": "string",
"description": "Offer to quote, e.g. ursy:email:email-solo"
},
"quantity": {
"type": "integer",
"description": "Whole number 1-100 (default 1)",
"minimum": 1,
"maximum": 100
}
},
"additionalProperties": false,
"required": [
"offer_id"
]
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"400": {
"description": "Malformed or invalid request (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"404": {
"description": "Not found (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"409": {
"description": "Conflict: expired quote, quote already ordered, or reused idempotency key",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"429": {
"description": "Rate limited; see Retry-After",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/quotes/{quote_id}": {
"get": {
"operationId": "ursyNetQuote",
"summary": "Read one issued quote and whether it has expired.",
"tags": [
"public"
],
"security": [],
"parameters": [
{
"name": "quote_id",
"in": "path",
"required": true,
"description": "Issued quote id",
"schema": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]+$"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"404": {
"description": "Not found (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/orders": {
"post": {
"operationId": "ursyNetOrders",
"summary": "Place an order against an unexpired quote. Validates fully, then refuses while ordering is closed. No charge is ever made by this route.",
"tags": [
"public"
],
"security": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"quote_id": {
"type": "string",
"description": "An unexpired quote id from POST /quotes"
},
"idempotency_key": {
"type": "string",
"description": "Optional client key (1-96 of A-Z a-z 0-9 . _ : -); a retry with the same key returns the same order",
"pattern": "^[A-Za-z0-9._:-]{1,96}$"
}
},
"additionalProperties": false,
"required": [
"quote_id"
]
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"400": {
"description": "Malformed or invalid request (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"401": {
"description": "No valid URSY.ID access token for this resource (typed: unauthorized, invalid_token or identity_not_configured)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"403": {
"description": "Refused: ordering_closed, or a token without the required scope",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"404": {
"description": "Not found (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"409": {
"description": "Conflict: expired quote, quote already ordered, or reused idempotency key",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
},
"/orders/{order_id}": {
"get": {
"operationId": "ursyNetOrder",
"summary": "Read one placed order. Requires an owner grant.",
"tags": [
"owner"
],
"security": [
{
"bearerAuth": [
"net.orders.read"
]
}
],
"parameters": [
{
"name": "order_id",
"in": "path",
"required": true,
"description": "Placed order id",
"schema": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]+$"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Envelope"
}
}
}
},
"401": {
"description": "No valid URSY.ID access token for this resource (typed: unauthorized, invalid_token or identity_not_configured)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"403": {
"description": "Refused: ordering_closed, or a token without the required scope",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"404": {
"description": "Not found (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"405": {
"description": "Method not allowed; the Allow header lists what this path accepts",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"500": {
"description": "Internal error (typed error)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"503": {
"description": "The authorization server could not be reached",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Envelope": {
"type": "object",
"required": [
"data",
"meta"
],
"properties": {
"data": {
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
},
"sponsor": {
"type": "object",
"description": "Paid placement (disclosure: paid placement). Only on GET /catalogue and GET /offers/{id}, only on sponsor days: message, name, url (a tracked link), sponsored, disclosure, rel, campaign_id, placement_id."
},
"sponsored_by": {
"type": "object",
"description": "The sponsor's entry doors, present only with sponsor."
}
}
},
"ErrorEnvelope": {
"type": "object",
"required": [
"error",
"meta"
],
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "string",
"description": "Stable machine code, e.g. offer_not_found, ordering_closed, quote_rate_limited"
},
"message": {
"type": "string"
},
"meta": {
"type": "object"
}
}
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
}
},
"JsonRpcRequest": {
"type": "object",
"required": [
"jsonrpc",
"method"
],
"properties": {
"jsonrpc": {
"type": "string",
"const": "2.0"
},
"id": {
"type": [
"string",
"integer",
"null"
]
},
"method": {
"type": "string",
"description": "initialize, tools/list, tools/call, ping, notifications/*"
},
"params": {
"type": "object"
}
}
},
"JsonRpcResponse": {
"type": "object",
"required": [
"jsonrpc"
],
"properties": {
"jsonrpc": {
"type": "string",
"const": "2.0"
},
"id": {
"type": [
"string",
"integer",
"null"
]
},
"result": {
"type": "object"
},
"error": {
"type": "object"
}
}
},
"Meta": {
"type": "object",
"required": [
"service",
"version",
"request_id"
],
"properties": {
"service": {
"type": "string",
"const": "ursy.net"
},
"version": {
"type": "string"
},
"request_id": {
"type": "string"
}
}
}
},
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"description": "URSY.ID access token for resource https://ursy.net/wp-json/ursy-net/v1"
}
}
}
}