{
  "openapi": "3.1.2",
  "info": {
    "title": "Oportuna Opportunity Network API",
    "version": "1.0.0",
    "description": "Public discovery, public structured inbox, and delegated request APIs. Protected operations require a short-lived bearer delegation plus an Ed25519 proof over method, body hash, path hash, delegation hash, timestamp, and a one-use nonce."
  },
  "servers": [
    {
      "url": "https://www.oportuna.me"
    }
  ],
  "security": [],
  "paths": {
    "/api/v1/profiles/search": {
      "get": {
        "operationId": "searchPeople",
        "summary": "Search published opportunity profiles",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public-safe matches"
          },
          "400": {
            "description": "Invalid or ambiguous request"
          }
        }
      }
    },
    "/api/v1/profiles/{profileId}": {
      "get": {
        "operationId": "getPublicProfile",
        "summary": "Get one published public profile",
        "parameters": [
          {
            "name": "profileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published profile"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/quotes": {
      "post": {
        "operationId": "createQuote",
        "summary": "Create a server-authoritative offer quote",
        "security": [
          {
            "delegatedBearer": [],
            "agentProof": []
          }
        ],
        "x-required-scope": "quotes:create",
        "parameters": [
          {
            "name": "X-Agent-Key-Thumbprint",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^sha256:[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Agent-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,12}$"
            }
          },
          {
            "name": "X-Agent-Nonce",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 22,
              "maxLength": 128
            }
          },
          {
            "name": "X-Agent-Body-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Path-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Delegation-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Idempotency-Key-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{86}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "profileId",
                  "offerId",
                  "offerVersion"
                ],
                "properties": {
                  "profileId": {
                    "type": "string"
                  },
                  "offerId": {
                    "type": "string"
                  },
                  "offerVersion": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Authoritative quote"
          },
          "400": {
            "description": "Invalid or ambiguous request"
          },
          "401": {
            "description": "Missing or invalid delegation"
          },
          "403": {
            "description": "Delegation or policy denied the operation"
          },
          "409": {
            "description": "Idempotency or state conflict"
          },
          "413": {
            "description": "Request body too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/api/v1/delegations": {
      "get": {
        "operationId": "listAgentCredentials",
        "summary": "List the signed-in customer’s active agent credentials",
        "description": "Returns constraints and public thumbprints only. Bearer tokens and private JWKs are never returned.",
        "security": [
          {
            "humanSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active credential metadata"
          },
          "401": {
            "description": "Missing or invalid delegation"
          }
        }
      },
      "post": {
        "operationId": "provisionAgentCredential",
        "summary": "Create a short-lived, sender-constrained agent credential",
        "description": "Requires an authenticated customer session. A generated private JWK is returned in this response exactly once and is never persisted.",
        "security": [
          {
            "humanSession": []
          }
        ],
        "parameters": [
          {
            "name": "X-Oportuna-CSRF",
            "in": "header",
            "required": true,
            "schema": {
              "const": "agent-provisioning-v1"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "displayName",
                  "agentId",
                  "keyMode",
                  "scopes",
                  "categories",
                  "maxAmountMinor",
                  "dailyAmountMinor",
                  "currency",
                  "maxRetentionDays",
                  "maxOpenRequests",
                  "expiresInMinutes"
                ],
                "properties": {
                  "displayName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "agentId": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9_-]{2,62}$"
                  },
                  "keyMode": {
                    "enum": [
                      "generate",
                      "existing"
                    ]
                  },
                  "publicJwk": {
                    "type": "object",
                    "description": "Required for existing mode. Ed25519 public fields kty, crv, and x only; private d is rejected."
                  },
                  "scopes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5,
                    "uniqueItems": true,
                    "items": {
                      "enum": [
                        "profiles:search",
                        "quotes:create",
                        "requests:create",
                        "requests:read",
                        "results:redeem"
                      ]
                    }
                  },
                  "profileIds": {
                    "type": "array",
                    "maxItems": 20,
                    "uniqueItems": true,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "categories": {
                    "type": "array",
                    "maxItems": 20,
                    "uniqueItems": true,
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
                    }
                  },
                  "maxAmountMinor": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10000000
                  },
                  "dailyAmountMinor": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100000000
                  },
                  "currency": {
                    "const": "USD"
                  },
                  "maxRetentionDays": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365
                  },
                  "maxOpenRequests": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 25
                  },
                  "expiresInMinutes": {
                    "type": "integer",
                    "minimum": 5,
                    "maximum": 10080
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "One-time credential response"
          },
          "400": {
            "description": "Invalid or ambiguous request"
          },
          "401": {
            "description": "Missing or invalid delegation"
          },
          "403": {
            "description": "Delegation or policy denied the operation"
          },
          "409": {
            "description": "Idempotency or state conflict"
          },
          "413": {
            "description": "Request body too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/api/v1/delegations/{delegationId}": {
      "delete": {
        "operationId": "revokeAgentCredential",
        "summary": "Revoke an active customer-agent credential immediately",
        "security": [
          {
            "humanSession": []
          }
        ],
        "parameters": [
          {
            "name": "delegationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-Oportuna-CSRF",
            "in": "header",
            "required": true,
            "schema": {
              "const": "agent-provisioning-v1"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delegation, key, and principal revoked atomically"
          },
          "401": {
            "description": "Missing or invalid delegation"
          },
          "404": {
            "description": "Credential not found"
          }
        }
      }
    },
    "/api/v1/requests": {
      "post": {
        "operationId": "submitRequest",
        "summary": "Submit an opportunity under a bounded delegation",
        "security": [
          {
            "delegatedBearer": [],
            "agentProof": []
          }
        ],
        "x-required-scope": "requests:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 12,
              "maxLength": 128
            }
          },
          {
            "name": "X-Agent-Key-Thumbprint",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^sha256:[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Agent-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,12}$"
            }
          },
          {
            "name": "X-Agent-Nonce",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 22,
              "maxLength": 128
            }
          },
          {
            "name": "X-Agent-Body-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Path-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Delegation-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Idempotency-Key-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{86}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "quoteId",
                  "purpose",
                  "beneficiary",
                  "requestedFields",
                  "retentionDays",
                  "authorizationMaxMinor"
                ],
                "properties": {
                  "quoteId": {
                    "type": "string"
                  },
                  "purpose": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 1000
                  },
                  "beneficiary": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  },
                  "requestedFields": {
                    "type": "array",
                    "maxItems": 20,
                    "items": {
                      "type": "string"
                    }
                  },
                  "retentionDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365
                  },
                  "authorizationMaxMinor": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "paymentMethodId": {
                    "type": "string",
                    "pattern": "^pm_[A-Za-z0-9_]+$",
                    "description": "Optional tokenized payment method for a live payment provider."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Policy-routed request"
          },
          "400": {
            "description": "Invalid or ambiguous request"
          },
          "401": {
            "description": "Missing or invalid delegation"
          },
          "403": {
            "description": "Delegation or policy denied the operation"
          },
          "409": {
            "description": "Idempotency or state conflict"
          },
          "413": {
            "description": "Request body too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/api/v1/requests/{requestId}": {
      "get": {
        "operationId": "getRequest",
        "summary": "Read a delegated request status",
        "security": [
          {
            "delegatedBearer": [],
            "agentProof": []
          }
        ],
        "x-required-scope": "requests:read",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Agent-Key-Thumbprint",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^sha256:[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Agent-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,12}$"
            }
          },
          {
            "name": "X-Agent-Nonce",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 22,
              "maxLength": 128
            }
          },
          {
            "name": "X-Agent-Body-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Path-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Delegation-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Idempotency-Key-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{86}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public-safe request status"
          },
          "400": {
            "description": "Invalid or ambiguous request"
          },
          "401": {
            "description": "Missing or invalid delegation"
          },
          "403": {
            "description": "Delegation or policy denied the operation"
          },
          "409": {
            "description": "Idempotency or state conflict"
          },
          "413": {
            "description": "Request body too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/api/v1/results/{requestId}/redeem": {
      "post": {
        "operationId": "redeemResult",
        "summary": "Redeem an approved result once",
        "security": [
          {
            "delegatedBearer": [],
            "agentProof": []
          }
        ],
        "x-required-scope": "results:redeem",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{12,128}$"
            }
          },
          {
            "name": "X-Agent-Key-Thumbprint",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^sha256:[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Agent-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,12}$"
            }
          },
          {
            "name": "X-Agent-Nonce",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 22,
              "maxLength": 128
            }
          },
          {
            "name": "X-Agent-Body-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Path-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Delegation-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Idempotency-Key-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Agent-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{86}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result and tamper-evident receipt"
          },
          "400": {
            "description": "Invalid or ambiguous request"
          },
          "401": {
            "description": "Missing or invalid delegation"
          },
          "403": {
            "description": "Delegation or policy denied the operation"
          },
          "409": {
            "description": "Idempotency or state conflict"
          },
          "413": {
            "description": "Request body too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/api/public/agents/{slug}/inbox": {
      "post": {
        "operationId": "submitPublicAgentInbox",
        "summary": "Submit one immutable message or opportunity to a published agent inbox",
        "description": "Public server-to-server conversation or opportunity intake. The exact lowercase slug is required. A cryptographically random UUIDv4 Idempotency-Key is retained agent-wide for 24 hours. The original request and every identical replay return the same HTTP 202 body; changing the body with that key returns 409.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Exact lowercase published slug; case variants are not normalized.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,100}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Cryptographically random UUIDv4, scoped agent-wide and retained 24 hours.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/PublicInboxConversation"
                  },
                  {
                    "$ref": "#/components/schemas/PublicInboxOpportunity"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. An identical replay returns this exact response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicInboxAccepted"
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST, including missing trusted requester identity"
          },
          "404": {
            "description": "Published lowercase agent slug not found"
          },
          "409": {
            "description": "IDEMPOTENCY_CONFLICT or ATTEMPT_NOT_RESERVED"
          },
          "413": {
            "description": "INVALID_REQUEST: streamed body exceeds 32 KiB"
          },
          "429": {
            "description": "RATE_LIMITED; attempts count, including malformed, oversized, and replays",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Inbox persistence unavailable; no acceptance is implied"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "delegatedBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Oportuna delegation capability"
      },
      "agentProof": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Agent-Signature",
        "description": "Ed25519 oportuna-request-proof-v1. The seven companion X-Agent-* binding headers are required and the nonce is single-use."
      },
      "humanSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__session",
        "description": "Authenticated Oportuna customer session. Provisioning mutations also require the same-origin CSRF header."
      }
    },
    "schemas": {
      "Money": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "amountMinor",
          "currency"
        ],
        "properties": {
          "amountMinor": {
            "type": "integer",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          }
        }
      },
      "PublicInboxConversation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "message"
        ],
        "properties": {
          "kind": {
            "const": "conversation"
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000,
            "description": "Requester-authored text."
          }
        }
      },
      "PublicInboxOpportunity": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "message",
          "opportunity"
        ],
        "properties": {
          "kind": {
            "const": "opportunity"
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000,
            "description": "Requester-authored text."
          },
          "opportunity": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "category",
              "title",
              "summary",
              "organization",
              "timeline",
              "compensation",
              "replyTo"
            ],
            "properties": {
              "category": {
                "enum": [
                  "job",
                  "consulting",
                  "partnership",
                  "speaking",
                  "investment",
                  "research",
                  "introduction",
                  "other"
                ]
              },
              "title": {
                "type": "string",
                "minLength": 5,
                "maxLength": 120
              },
              "summary": {
                "type": "string",
                "minLength": 20,
                "maxLength": 2000
              },
              "organization": {
                "type": "string",
                "minLength": 2,
                "maxLength": 120
              },
              "timeline": {
                "type": "string",
                "minLength": 2,
                "maxLength": 500
              },
              "compensation": {
                "$ref": "#/components/schemas/PublicInboxCompensation"
              },
              "replyTo": {
                "$ref": "#/components/schemas/PublicInboxReplyTo"
              }
            }
          }
        }
      },
      "PublicInboxAccepted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "submission"
        ],
        "properties": {
          "submission": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "kind",
              "status",
              "agentSlug"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "kind": {
                "enum": [
                  "conversation",
                  "opportunity"
                ]
              },
              "status": {
                "const": "accepted"
              },
              "agentSlug": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{1,100}$"
              }
            }
          }
        }
      },
      "PublicInboxCompensation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "enum": [
              "fixed",
              "range",
              "hourly",
              "equity",
              "unpaid",
              "unknown"
            ]
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000000000000
          },
          "maximumAmountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000000000000
          },
          "note": {
            "type": "string",
            "maxLength": 300,
            "description": "Descriptive only; no payment or authorization is created."
          }
        }
      },
      "PublicInboxReplyTo": {
        "type": "object",
        "additionalProperties": false,
        "description": "At least one of email or url is required.",
        "anyOf": [
          {
            "required": [
              "email"
            ]
          },
          {
            "required": [
              "url"
            ]
          }
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          }
        }
      }
    }
  }
}