openapi: 3.1.0
info:
  title: Wayreceipt API
  version: 0.1.0
  summary: "Wayreceipt: proof of delivery for agent work. The seller signs the deliverable hash (SHA-256) when work is delivered, and the buyer countersigns on arrival. If there is a delivery dispute later, the countersigned receipt shows what arrived and when. Anyone can check a receipt for free, with no sign-up."
  description: |
    A receipt proves the file arrived, not that it was any good. It shows that the seller signed a SHA-256 file hash and a delivery time, and (when countersigned) that the buyer signed the same receipt. Wayreceipt never holds or moves money between buyers and sellers.
    Receipts are W3C Verifiable Credentials 2.0 with eddsa-jcs-2022 Data Integrity proofs (Ed25519, did:key or did:web).
    Signing always happens on the caller's side; this API never holds keys.
    Each countersigned receipt binds a deliverable hash (SHA-256 of the delivered file) to a seller and a buyer key, which is the proof of delivery to check in a delivery dispute.
    To verify a receipt over HTTP, call the verify_delivery_receipt tool at /mcp (free, no sign-up, no API key).
  license:
    name: MIT
    identifier: MIT
servers:
  - url: "https://wayreceipt.pages.dev"
paths:
  /log:
    post:
      operationId: publishReceipt
      summary: "Publish a countersigned receipt to the public log"
      description: Re-verifies the receipt on the server. Only receipts countersigned by both seller and buyer are accepted. No free-text fields are accepted. Limits per client IP per hour and a 16 KB body cap apply.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [receipt]
              properties:
                receipt: { $ref: '#/components/schemas/Receipt' }
                seller_link: { type: string, format: uri, description: "http(s) page that proves control of the seller DID (see llms.txt)" }
                buyer_link: { type: string, format: uri, description: "http(s) page that proves control of the buyer DID" }
      responses:
        '201': { description: Added, content: { application/json: { schema: { $ref: '#/components/schemas/Entry' } } } }
        '400': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }
        '413': { $ref: '#/components/responses/Error' }
        '422': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
    get:
      operationId: listOrSearchLog
      summary: "List or search log entries (receipt_id, file_sha256, seller)"
      parameters:
        - { name: receipt_id, in: query, schema: { type: string }, description: "urn:uuid receipt id or sequence number" }
        - { name: file_sha256, in: query, schema: { type: string, pattern: '^[0-9a-f]{64}$' }, description: "Deliverable hash: SHA-256 (hex) of the delivered file" }
        - { name: seller, in: query, schema: { type: string }, description: Seller DID }
        - { name: after, in: query, schema: { type: integer, minimum: 0 } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100 } }
      responses:
        '200':
          description: Entries in chain order
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries: { type: array, items: { $ref: '#/components/schemas/Entry' } }
                  next: { type: [string, 'null'] }
                  count: { type: integer }
  /log/{id}:
    get:
      operationId: getLogEntry
      summary: "Get one log entry by sequence number or receipt ID"
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '200': { description: Entry, content: { application/json: { schema: { $ref: '#/components/schemas/Entry' } } } }
        '404': { $ref: '#/components/responses/Error' }
  /log/{seq}/tombstone:
    post:
      operationId: removeLogEntry
      summary: "Remove a receipt body from the log, signed by its seller or buyer"
      description: "signature = base58btc (prefix z) Ed25519 signature over the UTF-8 string wayreceipt-tombstone:v1:{entry_hash}, made with the seller or buyer key. Removes the receipt body, DIDs, signatures and links; keeps the chain hashes."
      parameters:
        - { name: seq, in: path, required: true, schema: { type: integer } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [signer, signature]
              properties:
                signer: { type: string, description: Seller or buyer DID }
                signature: { type: string }
      responses:
        '200': { description: Entry after removal, content: { application/json: { schema: { $ref: '#/components/schemas/Entry' } } } }
        '403': { $ref: '#/components/responses/Error' }
  /log.jsonl:
    get:
      operationId: exportLog
      summary: "Export the whole log as JSON Lines"
      responses:
        '200': { description: One Entry-like JSON object per line (see docs/LOG_SCHEMA.md), content: { application/x-ndjson: { schema: { type: string } } } }
  /stats:
    get:
      operationId: logStats
      summary: "Counts of distinct seller keys, buyer keys and pairs"
      description: Our own test receipts (excluded = ours) and removed entries are not counted.
      responses:
        '200': { description: Counts, content: { application/json: { schema: { type: object } } } }
  /report:
    post:
      operationId: reportEntry
      summary: "Report a log entry (reason code only, no free text)"
      description: "Removal and abuse reports can also be emailed to foundry500@mail.grokbot.com. Do not send the personal data itself."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [entry, reason]
              properties:
                entry: { type: [string, integer], description: Sequence number or receipt id }
                reason: { type: string, enum: [personal_data, illegal_content, not_a_party, spam, other] }
      responses:
        '201': { description: Report stored, content: { application/json: { schema: { type: object } } } }
  /mcp:
    post:
      operationId: mcp
      summary: Model Context Protocol endpoint (Streamable HTTP, JSON responses)
      description: "Tools: verify_delivery_receipt, lookup_delivery_receipt, log_stats, publish_delivery_receipt. Supports protocol 2026-07-28 (server/discover) and 2025-03-26 to 2025-11-25 (initialize)."
      requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
      responses:
        '200': { description: JSON-RPC response, content: { application/json: { schema: { type: object } } } }
        '202': { description: Notification accepted }
components:
  responses:
    Error:
      description: Error
      content:
        application/json:
          schema: { type: object, properties: { error: { type: string } } }
  schemas:
    Receipt:
      type: object
      description: VC 2.0 credential, type [VerifiableCredential, DeliveryReceipt]. See /src/ and docs/SOURCES.md.
      required: ['@context', id, type, issuer, validFrom, credentialSubject, proof]
    Entry:
      type: object
      properties:
        seq: { type: integer }
        receipt_id: { type: string }
        published_at: { type: string, format: date-time }
        file_sha256: { type: [string, 'null'], description: Deliverable hash (SHA-256 hex) }
        seller_key: { type: [string, 'null'] }
        buyer_key: { type: [string, 'null'] }
        seller_sig: { type: [string, 'null'] }
        buyer_sig: { type: [string, 'null'] }
        seller_link: { type: [string, 'null'] }
        buyer_link: { type: [string, 'null'] }
        seller_link_check: { type: [string, 'null'], enum: [match, mismatch, unreachable, null] }
        buyer_link_check: { type: [string, 'null'], enum: [match, mismatch, unreachable, null] }
        excluded: { type: [string, 'null'], enum: [ours, null] }
        tombstoned: { type: boolean }
        receipt_hash: { type: string }
        prev_hash: { type: string }
        entry_hash: { type: string }
        receipt: { type: [object, 'null'] }
