{ "openapi": "3.0.3", "info": { "title": "PicPeak API", "version": "v1", "description": "Public REST API for PicPeak — create gallery events, upload photos, fetch share links. Authenticate with a Bearer token issued via the admin **Settings → API Tokens** tab." }, "servers": [ { "url": "/api/v1", "description": "Same-origin (production)" } ], "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "pp_live_*", "description": "Long-lived API token. Issue via Settings → API Tokens. Token format: `pp_live_`. Scopes: `read`, `write`, `admin`." } }, "schemas": { "EventSummary": { "type": "object", "properties": { "id": { "type": "integer" }, "slug": { "type": "string" }, "event_name": { "type": "string" }, "event_type": { "type": "string" }, "event_date": { "type": "string", "format": "date", "nullable": true }, "expires_at": { "type": "string", "format": "date-time", "nullable": true }, "is_active": { "type": "boolean" }, "is_archived": { "type": "boolean" }, "is_draft": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" } } } } }, "security": [ { "bearerAuth": [] } ], "paths": { "/events": { "post": { "tags": [ "Events" ], "summary": "Create a gallery event", "description": "Returns the new event's id, slug, and absolute share URL.", "security": [ { "bearerAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "event_name", "event_type" ], "properties": { "event_name": { "type": "string" }, "event_type": { "type": "string", "enum": [ "wedding", "birthday", "corporate", "other", "family" ] }, "event_date": { "type": "string", "format": "date", "nullable": true }, "customer_name": { "type": "string", "nullable": true }, "customer_email": { "type": "string", "format": "email", "nullable": true }, "customer_phone": { "type": "string", "nullable": true, "description": "Only persisted when the global phone-field setting is enabled." }, "admin_email": { "type": "string", "format": "email", "nullable": true }, "require_password": { "type": "boolean", "default": true }, "password": { "type": "string", "nullable": true, "description": "Required when require_password is true." }, "expires_at": { "type": "string", "format": "date-time", "nullable": true } } } } } }, "responses": { "201": { "description": "Event created", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer" }, "slug": { "type": "string" }, "share_url": { "type": "string", "format": "uri" }, "share_token": { "type": "string" } } } } } }, "400": { "description": "Validation error" }, "401": { "description": "Missing/invalid token" }, "403": { "description": "Token lacks admin scope" } } }, "get": { "tags": [ "Events" ], "summary": "List gallery events (paginated)", "security": [ { "bearerAuth": [] } ], "parameters": [ { "in": "query", "name": "page", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "in": "query", "name": "limit", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } } ], "responses": { "200": { "description": "Paginated list", "content": { "application/json": { "schema": { "type": "object", "properties": { "events": { "type": "array", "items": { "$ref": "#/components/schemas/EventSummary" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" } } } } } } } } } } }, "/events/{id}": { "get": { "tags": [ "Events" ], "summary": "Get a single event", "security": [ { "bearerAuth": [] } ], "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Event details" }, "404": { "description": "Not found" } } } }, "/events/{id}/photos": { "post": { "tags": [ "Photos" ], "summary": "Upload a single photo to an event", "security": [ { "bearerAuth": [] } ], "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": [ "photo" ], "properties": { "photo": { "type": "string", "format": "binary" } } } } } }, "responses": { "201": { "description": "Photo uploaded", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer" }, "filename": { "type": "string" }, "path": { "type": "string" }, "thumbnail_path": { "type": "string", "nullable": true }, "size_bytes": { "type": "integer" } } } } } }, "400": { "description": "No file or invalid type" }, "404": { "description": "Event not found" } } } }, "/events/{id}/share-link": { "get": { "tags": [ "Events" ], "summary": "Get the absolute share URL for an event", "security": [ { "bearerAuth": [] } ], "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Share URL", "content": { "application/json": { "schema": { "type": "object", "properties": { "slug": { "type": "string" }, "share_token": { "type": "string" }, "share_url": { "type": "string", "format": "uri" } } } } } }, "404": { "description": "Not found" } } } } }, "tags": [] }