{ "openapi": "3.1.0", "info": { "title": "FOSSBilling Extensions API (v2)", "version": "2.0.0", "description": "Self-service extension publishing, ownership, moderation, and public browsing. v1 (/extensions/v1) remains available for existing integrations." }, "servers": [ { "url": "/extensions/v2" } ], "components": { "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer" } }, "schemas": { "OwnedExtensionListResponse": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/OwnedExtensionListItem" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }, "required": ["result", "pagination"] }, "OwnedExtensionListItem": { "type": "object", "properties": { "id": { "type": "string" }, "developer": { "$ref": "#/components/schemas/PublicDeveloper" }, "published": { "type": ["object", "null"], "properties": { "type": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "description": { "type": "string", "minLength": 1, "maxLength": 4000 }, "website": { "type": "string", "maxLength": 2048, "format": "uri" }, "license": { "$ref": "#/components/schemas/License" }, "icon_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "source": { "$ref": "#/components/schemas/Repository" }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" } }, "required": [ "type", "name", "description", "website", "license", "source", "version", "download_url" ] }, "pending_revision": { "$ref": "#/components/schemas/PendingRevisionRef" }, "last_review": { "$ref": "#/components/schemas/RevisionReview" }, "created_at": { "type": "string" }, "updated_at": { "type": "string" } }, "required": [ "id", "developer", "published", "pending_revision", "last_review", "created_at", "updated_at" ] }, "PublicDeveloper": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, "type": { "type": "string", "enum": ["user", "organization"] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "URL": { "type": "string", "maxLength": 2048, "format": "uri" }, "avatar_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "approved": { "type": "boolean" }, "unclaimed": { "type": "boolean" } }, "required": ["id", "type", "name", "approved", "unclaimed"] }, "License": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "spdx_id": { "type": "string", "description": "A current SPDX license identifier (https://spdx.org/licenses/). Omitted for custom or proprietary licenses.", "example": "MIT" }, "URL": { "type": "string", "maxLength": 2048, "format": "uri" } }, "required": ["name"], "additionalProperties": false }, "Repository": { "type": "object", "properties": { "type": { "type": "string", "enum": ["github", "gitlab", "custom"] }, "repo": { "type": "string", "minLength": 1, "maxLength": 500 } }, "required": ["type", "repo"], "additionalProperties": false }, "PendingRevisionRef": { "type": ["object", "null"], "properties": { "id": { "type": "string" }, "created_at": { "type": "string" } }, "required": ["id", "created_at"] }, "RevisionReview": { "type": ["object", "null"], "properties": { "revision_id": { "type": "string" }, "status": { "type": "string", "enum": ["approved", "rejected"] }, "review_note": { "type": ["string", "null"] }, "reviewed_at": { "type": ["string", "null"] } }, "required": ["revision_id", "status", "review_note", "reviewed_at"] }, "Pagination": { "type": "object", "properties": { "next_cursor": { "type": ["string", "null"] }, "has_more": { "type": "boolean" } }, "required": ["next_cursor", "has_more"] }, "Error": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "details": { "type": "array", "items": { "type": [ "string", "number", "boolean", "object", "array", "null" ] } } }, "required": ["message", "code"] } }, "required": ["error"] }, "OwnedExtension": { "allOf": [ { "$ref": "#/components/schemas/OwnedExtensionListItem" }, { "type": "object", "properties": { "published": { "allOf": [ { "$ref": "#/components/schemas/ExtensionContent" }, { "type": ["object", "null"], "properties": { "releases": { "type": "array", "items": { "$ref": "#/components/schemas/Release" }, "maxItems": 100 } } } ] }, "pending_revision": { "allOf": [ { "$ref": "#/components/schemas/PendingRevisionRef" }, { "type": ["object", "null"], "properties": { "content": { "$ref": "#/components/schemas/StoredExtensionContent" } }, "required": ["content"] } ] } } } ] }, "Release": { "type": "object", "properties": { "tag": { "type": "string", "minLength": 1, "maxLength": 100 }, "date": { "type": "string", "minLength": 1, "maxLength": 64 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "changelog_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "min_fossbilling_version": { "type": "string", "minLength": 1, "maxLength": 100 } }, "required": ["tag", "date", "download_url", "min_fossbilling_version"], "additionalProperties": false }, "ExtensionContent": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "description": { "type": "string", "minLength": 1, "maxLength": 4000 }, "releases": { "type": "array", "items": { "$ref": "#/components/schemas/Release" }, "minItems": 1, "maxItems": 100 }, "website": { "type": "string", "maxLength": 2048, "format": "uri" }, "license": { "$ref": "#/components/schemas/License" }, "icon_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "readme": { "type": "string", "minLength": 1, "maxLength": 100000 }, "source": { "$ref": "#/components/schemas/Repository" }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" } }, "required": [ "type", "name", "description", "releases", "website", "license", "readme", "source", "version", "download_url" ] }, "StoredExtensionContent": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "description": { "type": "string", "minLength": 1, "maxLength": 4000 }, "releases": { "type": "array", "items": { "$ref": "#/components/schemas/Release" }, "maxItems": 100 }, "website": { "type": "string", "maxLength": 2048, "format": "uri" }, "license": { "$ref": "#/components/schemas/License" }, "icon_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "readme": { "type": "string", "minLength": 1, "maxLength": 100000 }, "source": { "$ref": "#/components/schemas/Repository" }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" } } }, "ExtensionCreate": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "description": { "type": "string", "minLength": 1, "maxLength": 4000 }, "releases": { "type": "array", "items": { "$ref": "#/components/schemas/Release" }, "minItems": 1, "maxItems": 100 }, "website": { "type": "string", "maxLength": 2048, "format": "uri" }, "license": { "$ref": "#/components/schemas/License" }, "icon_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "readme": { "type": "string", "minLength": 1, "maxLength": 100000 }, "source": { "$ref": "#/components/schemas/Repository" }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" } }, "required": [ "type", "name", "description", "releases", "website", "license", "readme", "source", "version", "download_url", "id" ], "additionalProperties": false }, "ExtensionUpdate": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "description": { "type": "string", "minLength": 1, "maxLength": 4000 }, "releases": { "type": "array", "items": { "$ref": "#/components/schemas/Release" }, "minItems": 1, "maxItems": 100 }, "website": { "type": "string", "maxLength": 2048, "format": "uri" }, "license": { "$ref": "#/components/schemas/License" }, "icon_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "readme": { "type": "string", "minLength": 1, "maxLength": 100000 }, "source": { "$ref": "#/components/schemas/Repository" }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" } }, "required": [ "type", "name", "description", "releases", "website", "license", "readme", "source", "version", "download_url" ], "additionalProperties": false }, "ExtensionRevision": { "type": "object", "properties": { "id": { "type": "string" }, "extension_id": { "type": "string" }, "developer_id": { "type": "string" }, "submitted_by": { "type": "string" }, "status": { "type": "string", "enum": ["pending", "approved", "rejected"] }, "content": { "$ref": "#/components/schemas/StoredExtensionContent" }, "reviewer_id": { "type": ["string", "null"] }, "review_note": { "type": ["string", "null"] }, "created_at": { "type": "string" }, "reviewed_at": { "type": ["string", "null"] } }, "required": [ "id", "extension_id", "developer_id", "submitted_by", "status", "content", "reviewer_id", "review_note", "created_at", "reviewed_at" ] }, "ExtensionListResponse": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/ExtensionListItem" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }, "required": ["result", "pagination"] }, "ExtensionListItem": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "description": { "type": "string", "minLength": 1, "maxLength": 4000 }, "website": { "type": "string", "maxLength": 2048, "format": "uri" }, "license": { "$ref": "#/components/schemas/License" }, "icon_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "source": { "$ref": "#/components/schemas/Repository" }, "version": { "type": "string", "minLength": 1, "maxLength": 100 }, "download_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "id": { "type": "string" }, "developer": { "$ref": "#/components/schemas/PublicDeveloper" } }, "required": [ "type", "name", "description", "website", "license", "source", "version", "download_url", "id", "developer" ] }, "Extension": { "allOf": [ { "$ref": "#/components/schemas/ExtensionContent" }, { "type": "object", "properties": { "id": { "type": "string" }, "developer": { "$ref": "#/components/schemas/PublicDeveloper" } }, "required": ["id", "developer"] } ] }, "User": { "type": "object", "properties": { "display_name": { "type": ["string", "null"] }, "is_moderator": { "type": "boolean" }, "github_linked": { "type": "boolean" }, "active": { "type": "boolean" } }, "required": ["display_name", "is_moderator", "github_linked", "active"] }, "UserIdentityInput": { "type": "object", "properties": { "name": { "type": ["string", "null"], "maxLength": 200 }, "email": { "type": ["string", "null"], "maxLength": 254, "format": "email" }, "email_verified": { "type": "boolean" }, "picture": { "type": ["string", "null"], "maxLength": 2048 }, "github_login": { "type": ["string", "null"], "maxLength": 200 }, "github_orgs": { "type": ["array", "null"], "items": { "type": "string", "maxLength": 200 }, "maxItems": 500 }, "github_orgs_expires_at": { "type": ["string", "null"], "maxLength": 64 } }, "required": [ "name", "email", "email_verified", "picture", "github_login", "github_orgs", "github_orgs_expires_at" ], "additionalProperties": false }, "UserProfileUpdate": { "type": "object", "properties": { "display_name": { "type": ["string", "null"], "maxLength": 120 } }, "required": ["display_name"], "additionalProperties": false }, "DeveloperClaim": { "type": "object", "properties": { "id": { "type": "string" }, "developer_id": { "type": "string" }, "claimant_id": { "type": "string" }, "status": { "type": "string", "enum": ["pending", "approved", "rejected"] }, "note": { "type": "string" }, "review_note": { "type": "string" }, "reviewer_id": { "type": "string" }, "created_at": { "type": "string" }, "reviewed_at": { "type": "string" }, "github_org_verified": { "type": "boolean" }, "github_verification_note": { "type": "string" } }, "required": [ "id", "developer_id", "claimant_id", "status", "created_at" ] }, "ClaimNote": { "type": "object", "properties": { "note": { "type": "string", "maxLength": 500 } }, "additionalProperties": false }, "PendingDeveloperClaim": { "allOf": [ { "$ref": "#/components/schemas/DeveloperClaim" }, { "type": "object", "properties": { "developer_name": { "type": "string" }, "developer_type": { "type": "string", "enum": ["user", "organization"] }, "claimant_name": { "type": ["string", "null"] }, "claimant_github_login": { "type": ["string", "null"] } }, "required": [ "developer_name", "developer_type", "claimant_name", "claimant_github_login" ] } ] }, "DeveloperProfile": { "allOf": [ { "$ref": "#/components/schemas/Developer" }, { "type": "object", "properties": { "approved": { "type": "boolean" }, "content_revision": { "type": "integer", "exclusiveMinimum": 0 }, "github_org_verified": { "type": "boolean" }, "github_verification_note": { "type": "string" }, "github_verified_at": { "type": ["string", "null"] }, "github_url_verified": { "type": "boolean" }, "unclaimed": { "type": "boolean" }, "owner_name": { "type": ["string", "null"] }, "owner_github_login": { "type": ["string", "null"] } }, "required": ["approved", "content_revision"] } ] }, "Developer": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, "type": { "type": "string", "enum": ["user", "organization"] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "URL": { "type": "string", "maxLength": 2048, "format": "uri" }, "avatar_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "contact_email": { "type": "string", "maxLength": 254, "format": "email" } }, "required": ["id", "type", "name"] }, "ReviewNoteRequired": { "type": "object", "properties": { "review_note": { "type": "string", "minLength": 1, "maxLength": 2000 } }, "required": ["review_note"], "additionalProperties": false }, "DeveloperTransfer": { "type": "object", "properties": { "token": { "type": "string" }, "expires_at": { "type": "string" } }, "required": ["token", "expires_at"] }, "TransferAcceptance": { "type": "object", "properties": { "token": { "type": "string", "minLength": 64, "maxLength": 128 } }, "required": ["token"], "additionalProperties": false }, "ReviewNoteOptional": { "type": "object", "properties": { "review_note": { "type": "string", "maxLength": 2000 } }, "additionalProperties": false }, "DeveloperApproval": { "type": "object", "properties": { "expected_revision": { "type": "integer", "exclusiveMinimum": 0 } }, "required": ["expected_revision"], "additionalProperties": false }, "DeveloperHistoryEntry": { "type": "object", "properties": { "developer_id": { "type": "string" }, "type": { "type": "string", "enum": ["user", "organization"] }, "name": { "type": "string" }, "URL": { "type": "string", "maxLength": 2048, "format": "uri" }, "changed_by": { "type": "string" }, "changed_by_name": { "type": ["string", "null"] }, "changed_at": { "type": "string" } }, "required": [ "developer_id", "type", "name", "changed_by", "changed_by_name", "changed_at" ] }, "OwnedDeveloperProfile": { "anyOf": [ { "allOf": [ { "$ref": "#/components/schemas/DeveloperProfile" }, { "type": "object", "properties": { "has_pending_transfer": { "type": "boolean" } }, "required": ["has_pending_transfer"] } ] }, { "type": "null" } ] }, "DeveloperInput": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, "type": { "type": "string", "enum": ["user", "organization"] }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "URL": { "type": "string", "maxLength": 2048, "format": "uri" }, "avatar_url": { "type": "string", "maxLength": 2048, "format": "uri" }, "contact_email": { "type": "string", "maxLength": 254, "format": "email" } }, "required": ["id", "type", "name"], "additionalProperties": false } }, "parameters": {} }, "paths": { "/extensions/mine": { "get": { "tags": ["Extensions"], "summary": "List the caller's extensions, published or not", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 1000, "description": "Opaque cursor returned by the previous page" }, "required": false, "description": "Opaque cursor returned by the previous page", "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "Every extension under the caller's developer profile, each with its live content, any unreviewed edit, and the last moderator decision", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OwnedExtensionListResponse" } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Pagination query failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/extensions/mine/{id}": { "get": { "tags": ["Extensions"], "summary": "Get one of the caller's extensions, published or not", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "The extension's live content, its unreviewed edit if any, and the last moderator decision", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/OwnedExtension" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or the caller does not own this extension", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No extension with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/extensions": { "post": { "tags": ["Extensions"], "summary": "Create an extension and submit its first version for review", "security": [ { "Bearer": [] } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionCreate" } } } }, "responses": { "201": { "description": "Extension created. It holds the id immediately but stays out of the public catalogue until a moderator approves the revision.", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "revision_id": { "type": "string" }, "status": { "type": "string", "enum": ["pending"] } }, "required": ["id", "revision_id", "status"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or the caller has no developer profile to publish under", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "The id is taken, ownership changed, or the pending-revision limit was reached", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Body failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "get": { "tags": ["Extensions"], "summary": "List published extensions", "parameters": [ { "schema": { "type": "string", "enum": [ "mod", "theme", "payment-gateway", "server-manager", "domain-registrar", "hook", "translation" ] }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "developer_id", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 1000, "description": "Opaque cursor returned by the previous page" }, "required": false, "description": "Opaque cursor returned by the previous page", "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "Extensions matching the given filters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionListResponse" } } } }, "422": { "description": "Filter or pagination query failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/extensions/{id}": { "put": { "tags": ["Extensions"], "summary": "Submit an edit to an extension the caller owns", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionUpdate" } } } }, "responses": { "202": { "description": "Edit accepted as a pending revision. The published content is unchanged until a moderator approves it.", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "revision_id": { "type": "string" }, "status": { "type": "string", "enum": ["pending"] } }, "required": ["id", "revision_id", "status"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or the caller does not own this extension", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No extension with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "An edit is already awaiting review, or the pending-revision limit was reached", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Body failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "delete": { "tags": ["Extensions"], "summary": "Withdraw an extension that has never been published", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Extension and its revisions deleted, and the id released", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "deleted": { "type": "boolean", "enum": [true] } }, "required": ["id", "deleted"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or the caller does not own this extension", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No extension with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "The extension is published and cannot be withdrawn", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "get": { "tags": ["Extensions"], "summary": "Get a single published extension", "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "The extension", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/Extension" } }, "required": ["result"] } } } }, "404": { "description": "No extension with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/extensions/{id}/revisions": { "get": { "tags": ["Extensions"], "summary": "List an extension's revisions, newest first", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 1000 }, "required": false, "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "Every version proposed for this extension, with its review outcome", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/ExtensionRevision" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }, "required": ["result", "pagination"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or the caller neither owns this extension nor moderates", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No extension with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Pagination query failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/users/me/identity": { "put": { "tags": ["Users"], "summary": "Synchronize the caller's OIDC identity projection", "security": [ { "Bearer": [] } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserIdentityInput" } } } }, "responses": { "200": { "description": "Identity projection synchronized", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/User" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "Identity synchronization requires a trusted assertion", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Identity payload failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/users/me": { "get": { "tags": ["Users"], "summary": "Get the caller's account projection", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "The caller's account projection, including active status", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/User" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Account does not exist", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "patch": { "tags": ["Users"], "summary": "Update the caller's personal profile", "security": [ { "Bearer": [] } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileUpdate" } } } }, "responses": { "200": { "description": "Profile updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "display_name": { "type": ["string", "null"] } }, "required": ["display_name"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Account does not exist or has been deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "delete": { "tags": ["Users"], "summary": "Delete the caller's account and tombstone its user row", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "Account deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "deleted": { "type": "boolean", "enum": [true] } }, "required": ["deleted"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Account does not exist", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Account still owns protected domain records", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/{id}/claim": { "post": { "tags": ["Developers"], "summary": "Request ownership of an unowned developer profile", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClaimNote" } } } }, "responses": { "201": { "description": "Claim created and pending moderator review", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperClaim" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or the caller's linked GitHub account doesn't match this developer's GitHub organization or username", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No developer with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Profile is already owned, caller already owns a different profile, or already has a pending claim on this one", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "The request failed validation, or the GitHub account type is unsupported", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "GitHub verification is temporarily rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "GitHub verification is temporarily unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/claims/{id}/cancel": { "post": { "tags": ["Developers"], "summary": "Withdraw the caller's own pending profile claim", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Claim withdrawn", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "cancelled": { "type": "boolean", "enum": [true] } }, "required": ["id", "cancelled"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No pending claim with that id owned by the caller", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/claims/mine": { "get": { "tags": ["Developers"], "summary": "List the caller's own profile claims, in any status", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "The caller's claims", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/DeveloperClaim" } } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/claims": { "get": { "tags": ["Moderation"], "summary": "List pending profile claims", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "Claims awaiting moderator review", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/PendingDeveloperClaim" } } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/claims/{id}/approve": { "post": { "tags": ["Moderation"], "summary": "Approve a pending profile claim", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Claim approved; profile ownership transferred to the claimant", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperProfile" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No claim or developer with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Claim is no longer pending, profile is no longer unowned, or the claimant now owns a different profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/claims/{id}/reject": { "post": { "tags": ["Moderation"], "summary": "Reject a pending profile claim", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewNoteRequired" } } } }, "responses": { "200": { "description": "Claim rejected", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperClaim" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No pending claim with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param or review_note body failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/{id}/transfer": { "post": { "tags": ["Developers"], "summary": "Create a single-use link to hand this profile to another account", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Transfer token created; share it out-of-band with the recipient", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperTransfer" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller does not own this profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No developer with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/{id}/transfer/revoke": { "post": { "tags": ["Developers"], "summary": "Revoke this profile's pending transfer link, if any", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Any pending transfer for this profile is revoked", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "revoked": { "type": "boolean", "enum": [true] } }, "required": ["id", "revoked"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller does not own this profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No developer with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/transfers/accept": { "post": { "tags": ["Developers"], "summary": "Accept a developer profile transfer using its single-use token", "security": [ { "Bearer": [] } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TransferAcceptance" } } } }, "responses": { "200": { "description": "Profile is now owned by the caller", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperProfile" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Transfer link is invalid, already used, or expired", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Caller already owns a different developer profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "token body failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/moderation/extensions": { "get": { "tags": ["Moderation"], "summary": "List extension revisions awaiting review", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "enum": ["pending", "approved", "rejected"] }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 1000 }, "required": false, "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "Revisions matching the requested status (default: pending), oldest first", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/ExtensionRevision" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }, "required": ["result", "pagination"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "status query param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/extensions/{id}/revisions/{revisionId}/approve": { "post": { "tags": ["Moderation"], "summary": "Approve a pending revision and publish it", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "acme-gateway" }, "required": true, "name": "id", "in": "path" }, { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "revisionId", "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewNoteOptional" } } } }, "responses": { "200": { "description": "Revision approved and published as the extension's live content", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "status": { "type": "string", "enum": ["approved"] } }, "required": ["id", "status"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No such revision on that extension", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Revision is not pending, or ownership has changed since it was proposed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Path params or review_note body failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/extensions/{id}/revisions/{revisionId}/reject": { "post": { "tags": ["Moderation"], "summary": "Reject a pending revision", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "acme-gateway" }, "required": true, "name": "id", "in": "path" }, { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "revisionId", "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewNoteRequired" } } } }, "responses": { "200": { "description": "Revision rejected. The extension's published content is unchanged.", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "status": { "type": "string", "enum": ["rejected"] } }, "required": ["id", "status"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No such revision on that extension", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Revision is not pending", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "review_note is required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers": { "get": { "tags": ["Moderation"], "summary": "List every developer profile, approved or not", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "All developer profiles", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/DeveloperProfile" } } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/unapproved": { "get": { "tags": ["Moderation"], "summary": "List developer profiles awaiting moderator review", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "Developer profiles not yet approved", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/DeveloperProfile" } } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/{id}/approve": { "post": { "tags": ["Moderation"], "summary": "Mark a developer profile as reviewed/approved", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeveloperApproval" } } } }, "responses": { "200": { "description": "Developer profile marked approved", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "approved": { "type": "boolean", "enum": [true] } }, "required": ["id", "approved"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "No developer with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Profile changed after the reviewed revision", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/{id}/history": { "get": { "tags": ["Moderation"], "summary": "List the write history of a developer profile", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Snapshots of the profile, newest first", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "array", "items": { "$ref": "#/components/schemas/DeveloperHistoryEntry" } } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive or the caller is not a moderator", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/me": { "get": { "tags": ["Developers"], "summary": "Get the caller's own developer profile", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "The caller's profile, or null when none exists", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/OwnedDeveloperProfile" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "put": { "tags": ["Developers"], "summary": "Create or update the caller's own developer profile", "security": [ { "Bearer": [] } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeveloperInput" } } } }, "responses": { "200": { "description": "Developer profile created or updated and usable immediately", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperProfile" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The account is inactive, or this id matches a real GitHub organization or username that isn't linked to the caller's account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Developer id already taken by someone else, or id was changed on an existing profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "Payload failed validation, or the GitHub account type is unsupported", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "The account exhausted its profile-creation allowance, or GitHub verification is temporarily rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "GitHub verification is temporarily unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "delete": { "tags": ["Developers"], "summary": "Permanently delete the caller's own developer profile", "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "Profile deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "object", "properties": { "id": { "type": "string" }, "deleted": { "type": "boolean", "enum": [true] } }, "required": ["id", "deleted"] } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Caller has no developer profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Profile still has extensions attached, published or not", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/me/reverify": { "post": { "tags": ["Developers"], "summary": "Re-check the caller's linked GitHub identity against their own developer profile", "security": [ { "Bearer": [] } ], "parameters": [ { "schema": { "type": "string", "enum": ["true", "false"] }, "required": false, "name": "check_url", "in": "query" } ], "responses": { "200": { "description": "Verification re-checked (result may be verified or not)", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/DeveloperProfile" } }, "required": ["result"] } } } }, "401": { "description": "Missing or invalid bearer token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The bearer is valid but the account is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Caller has no developer profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Developer ownership changed while re-verifying", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "The GitHub account type is unsupported", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "check_url was used again too soon, or GitHub verification is rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "GitHub verification is temporarily unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/developers/{id}": { "get": { "tags": ["Developers"], "summary": "Get a developer's public profile", "parameters": [ { "schema": { "type": "string", "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "The developer's public profile", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "$ref": "#/components/schemas/PublicDeveloper" } }, "required": ["result"] } } } }, "404": { "description": "No developer with that id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "422": { "description": "id param failed validation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "Database error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "webhooks": {} }