{
  "openapi": "3.1.0",
  "info": {
    "title": "PinOnce",
    "version": "1.0.0",
    "description": "Pin notes and photos on the user's own PinOnce page (pinonce.com/<nickname>), copied to their connected networks with a link back; save links they liked elsewhere to a private, searchable list. Pinning is public: confirm text, photos and networks with the user first. Auth: an agent key from pinonce.com/admin/ as a Bearer token."
  },
  "servers": [{ "url": "https://pinonce.com" }],
  "components": {
    "securitySchemes": { "agentKey": { "type": "http", "scheme": "bearer", "description": "Agent key (pa_...) from pinonce.com/admin/ → AI assistants" } },
    "schemas": {
      "Photo": { "type": "object", "properties": {
        "url": { "type": "string", "description": "Public http(s) address of a JPEG, PNG or WebP image (max 25 MB; under 1 MB for Bluesky)" },
        "base64": { "type": "string", "description": "Or the image bytes, base64" },
        "alt": { "type": "string", "description": "Short description for screen readers" } } },
      "Networks": { "type": "array", "items": { "type": "string", "enum": ["bluesky", "mastodon", "pixelfed", "instagram", "threads"] },
        "description": "Omit = every connected network that takes this kind of pin; [] = only the user's page" },
      "Error": { "type": "object", "properties": { "error": { "type": "string" } } }
    }
  },
  "security": [{ "agentKey": [] }],
  "paths": {
    "/api/me": { "get": { "operationId": "whoami", "summary": "The account, page address and storage used",
      "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "type": "object", "properties": {
        "handle": { "type": "string" }, "page": { "type": "string" }, "used_bytes": { "type": "integer" }, "quota_bytes": { "type": "integer" } } } } } } } } },
    "/api/v1/pins": {
      "get": { "operationId": "listPins", "summary": "Recent pins with where each was copied",
        "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 20 } }],
        "responses": { "200": { "description": "Pins", "content": { "application/json": { "schema": { "type": "object" } } } } } },
      "post": { "operationId": "createPin", "summary": "Pin a note or photos (public, right away)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["kind"], "properties": {
          "kind": { "type": "string", "enum": ["note", "photo"] },
          "title": { "type": "string", "description": "Required for photos" },
          "text": { "type": "string", "description": "The note, or notes under the photos (up to 5000 characters)" },
          "place": { "type": "string" }, "date": { "type": "string", "description": "YYYY-MM-DD" },
          "photos": { "type": "array", "minItems": 1, "maxItems": 10, "items": { "$ref": "#/components/schemas/Photo" } },
          "networks": { "$ref": "#/components/schemas/Networks" } } } } } },
        "responses": {
          "200": { "description": "Pinned", "content": { "application/json": { "schema": { "type": "object", "properties": {
            "id": { "type": "integer" }, "url": { "type": "string" }, "syndicating": { "type": "array", "items": { "type": "string" } } } } } } },
          "400": { "description": "Bad input", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "description": "Over the storage limit", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } },
    "/api/saves": {
      "get": { "operationId": "searchSaved", "summary": "Search the private Saved list",
        "parameters": [{ "name": "q", "in": "query", "schema": { "type": "string" } }, { "name": "tag", "in": "query", "schema": { "type": "string" } },
                       { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20 } }],
        "responses": { "200": { "description": "Saved links", "content": { "application/json": { "schema": { "type": "object" } } } } } },
      "post": { "operationId": "saveLink", "summary": "Save a link the user liked elsewhere",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url"], "properties": {
          "url": { "type": "string", "description": "The link, or shared text containing it" }, "note": { "type": "string" }, "tags": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Saved", "content": { "application/json": { "schema": { "type": "object" } } } } } } },
    "/api/syndicate": { "post": { "operationId": "resendPin", "summary": "Send (or retry) one pin to one connected network",
      "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["id", "network"], "properties": {
        "id": { "type": "integer" }, "network": { "type": "string", "enum": ["bluesky", "mastodon", "pixelfed", "instagram", "threads"] } } } } } },
      "responses": { "200": { "description": "Result", "content": { "application/json": { "schema": { "type": "object" } } } } } } }
  }
}
