"""
UQP GraphQL reference, not production. Nothing deployed; no UCP conformance claim.
Tattoo API SDL was not available in this checkout: operations mirror REST/MCP 1:1.
CamelCase transport fields, ID identifiers and explicit mutation input wrappers.
Core objects are JSON-valued custom scalars, NOT duplicated GraphQL field trees.
Their specifiedBy URLs identify the authoritative draft 2020-12 JSON Schemas in
spec/schemas. Values preserve canonical field names (including snake_case),
constraints and common.json definitions. Optional fields include attribution,
quantity/quantity_unit, deposit capture_on/refundable_until/policy_url, minor-unit
amounts, manifest review_sla_seconds/routing_policy and Quote checkout_handoff.
Quote id/expires_at are optional handoff aliases; valid_until remains required.
Select each scalar as a whole; validate
inputs AND outputs locally against its referenced schema. No network fetches.
PricingModel is referenced by manifest.pricing_model_ref; no retrieval operation.
LifecycleState projects lifecycle.json enum; spec/lifecycle.ts enforces transitions.
"""
scalar QuoteManifest @specifiedBy(url: "https://uqp.dev/spec/schemas/quote-manifest.json")
scalar QuoteRequest @specifiedBy(url: "https://uqp.dev/spec/schemas/quote-request.json")
scalar PricingModel @specifiedBy(url: "https://uqp.dev/spec/schemas/pricing-model.json")
scalar Quote @specifiedBy(url: "https://uqp.dev/spec/schemas/quote.json")
scalar Estimate @specifiedBy(url: "https://uqp.dev/spec/schemas/estimate.json")
scalar ClientApproval @specifiedBy(url: "https://uqp.dev/spec/schemas/common.json#/$defs/clientApproval")
"""Exact projection of spec/schemas/lifecycle.json; not a separate state machine."""
enum LifecycleState {
INITIATED
MANIFEST_ISSUED
PAYLOAD_SUBMITTED
MUTATION_REQUIRED
VALIDATED_PENDING_REVIEW
QUOTED
ACCEPTED
DECLINED
EXPIRED
}
"""Transport-only handoff. UQP never holds funds; provider is merchant of record."""
enum CheckoutProtocol { ucp acp }
enum UcpQuoteStatus { pending quoted accepted declined expired withdrawn }
type Checkout {
protocol: CheckoutProtocol!
"""The local reference returns null, not a working checkout URL."""
url: String
"""Mapping to draft proposals; subject to change. No conformance claim."""
ucpQuoteStatus: UcpQuoteStatus
acpQuoteId: ID
quoteExpiresAt: String
}
"""Transport envelope, not a new core object. Estimate prices are example data."""
type QuoteResult {
id: ID!
state: LifecycleState!
estimate: Estimate!
quote: Quote
checkout: Checkout
}
input RequestQuoteInput { request: QuoteRequest! }
input ReviewQuoteInput { validUntil: String! }
input AcceptQuoteInput { clientApproval: ClientApproval! }
type Query {
manifest: QuoteManifest!
"""Poll promptly; no long-held call or implemented webhook."""
quote(id: ID!): QuoteResult!
}
"""
All mutations require idempotencyKey (REST Idempotency-Key / MCP idempotency_key).
Scope: operation + target. Object order ignored, array order significant. Replay
returns the original immutable success; changed body gives conflict. Failed calls
reserve no key. Reference storage is in memory, not durable/tenant-scoped.
Errors use extensions {code, status, current_state?}; conflicts have status 409.
OAuth 2.0 is described only, not implemented. No request/PII logging.
"""
type Mutation {
"""Submit then poll; returns PAYLOAD_SUBMITTED with non-binding Estimate."""
requestQuote(input: RequestQuoteInput!, idempotencyKey: String!): QuoteResult!
"""Correct only MUTATION_REQUIRED, preserve ID, then poll again."""
resubmitQuote(id: ID!, input: RequestQuoteInput!, idempotencyKey: String!): QuoteResult!
"""Trusted host artist context only, denied by default; client cannot grant it."""
reviewQuote(id: ID!, input: ReviewQuoteInput!, idempotencyKey: String!): QuoteResult!
"""
Only unexpired QUOTED → ACCEPTED; cannot issue a Quote. Explicit clientApproval
covers exact deposit amount, currency, policies and approved_at via common.json.
Checkout handoff only. Deposit payment requires host-only verified processor
confirmation; no client mutation or input field can mark a deposit paid.
"""
acceptQuote(id: ID!, input: AcceptQuoteInput!, idempotencyKey: String!): QuoteResult!
declineQuote(id: ID!, idempotencyKey: String!): QuoteResult!
}