{"openapi":"3.1.0","info":{"title":"Pombo API","version":"1.0.0","description":"The Pombo API manages newsletters, draft issues, and bookmarks. Clients\nobtain bearer tokens using the browser-based device authorization flow.\n","contact":{"email":"support@spektrhq.com"}},"servers":[{"url":"https://pombomailer.com","description":"Production"},{"url":"http://localhost:3000","description":"Local development"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Authentication"},{"name":"Account"},{"name":"Newsletters"},{"name":"Issues"},{"name":"Bookmarks"}],"paths":{"/api/v1/auth/device":{"post":{"tags":["Authentication"],"summary":"Start device authorization","operationId":"startDeviceAuthorization","security":[],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string","maxLength":100,"default":"Pombo CLI"}}},"example":{"client_name":"Pombo CLI"}}}},"responses":{"201":{"description":"Device authorization created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorization"}}}}}}},"/api/v1/auth/token":{"post":{"tags":["Authentication"],"summary":"Exchange an authorized device code for a token","operationId":"exchangeDeviceCode","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["device_code"],"properties":{"device_code":{"type":"string"}}},"example":{"device_code":"device-code-from-the-authorization-response"}}}},"responses":{"200":{"description":"Device code exchanged successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}},"400":{"description":"Authorization pending, denied, expired, invalid, or already consumed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"pending":{"value":{"error":"authorization_pending","error_description":"The user has not authorized this request yet."}},"denied":{"value":{"error":"access_denied","error_description":"The user denied this request."}}}}}}}},"delete":{"tags":["Authentication"],"summary":"Revoke the current bearer token","operationId":"revokeToken","responses":{"204":{"description":"Token revoked"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/me":{"get":{"tags":["Account"],"summary":"Get the authenticated account","operationId":"getCurrentAccount","responses":{"200":{"description":"Current account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/newsletters":{"get":{"tags":["Newsletters"],"summary":"List accessible newsletters","description":"Returns newsletters owned by the account or shared with it for editing.","operationId":"listNewsletters","responses":{"200":{"description":"Accessible newsletters","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Newsletter"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/newsletters/{newsletter_id}/issues":{"parameters":[{"$ref":"#/components/parameters/NewsletterId"}],"get":{"tags":["Issues"],"summary":"List newsletter issues","description":"Issues are sorted newest first by created_at.","operationId":"listIssues","responses":{"200":{"description":"Newsletter issues","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Issue"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"tags":["Issues"],"summary":"Create a draft issue","description":"API-created issues always have draft status.","operationId":"createIssue","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueRequestEnvelope"},"example":{"issue":{"subject":"A new issue","content":"\u003cp\u003eHello subscribers.\u003c/p\u003e"}}}}},"responses":{"201":{"description":"Draft created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Issue"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"}}}},"/api/v1/newsletters/{newsletter_id}/issues/{issue_id}":{"parameters":[{"$ref":"#/components/parameters/NewsletterId"},{"$ref":"#/components/parameters/IssueId"}],"patch":{"tags":["Issues"],"summary":"Update an issue","description":"Issue status cannot be changed through this endpoint.","operationId":"updateIssue","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueRequestEnvelope"},"example":{"issue":{"subject":"An updated subject","meta_title":"Search-friendly title","content":"\u003cp\u003eUpdated issue body.\u003c/p\u003e"}}}}},"responses":{"200":{"description":"Updated issue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Issue"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"}}}},"/api/v1/newsletters/{newsletter_id}/bookmarks":{"parameters":[{"$ref":"#/components/parameters/NewsletterId"}],"get":{"tags":["Bookmarks"],"summary":"Get newsletter bookmarks","operationId":"getBookmarks","responses":{"200":{"description":"Bookmarks stored as HTML","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bookmarks"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Pombo API token","description":"Token returned by the device authorization flow."}},"parameters":{"NewsletterId":{"name":"newsletter_id","in":"path","required":true,"description":"Newsletter UUID returned by the newsletters endpoint.","schema":{"type":"string","format":"uuid"}},"IssueId":{"name":"issue_id","in":"path","required":true,"description":"Issue UUID returned by the issues endpoint.","schema":{"type":"string","format":"uuid"}}},"responses":{"Unauthorized":{"description":"A valid bearer token was not provided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized","error_description":"A valid bearer token is required."}}}},"NotFound":{"description":"The resource does not exist or is not accessible to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"The requested resource was not found."}}}},"BadRequest":{"description":"The request body is missing or malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationFailed":{"description":"The submitted resource was invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}},"schemas":{"DeviceAuthorization":{"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"],"properties":{"device_code":{"type":"string","description":"Secret temporary code used only by the client while polling."},"user_code":{"type":"string"},"verification_uri":{"type":"string","format":"uri"},"verification_uri_complete":{"type":"string","format":"uri","description":"Open this URL to authorize the client in a browser."},"expires_in":{"type":"integer","example":600},"interval":{"type":"integer","description":"Minimum polling interval in seconds.","example":5}}},"Token":{"type":"object","required":["access_token","token_type"],"properties":{"access_token":{"type":"string","readOnly":true,"example":"pombo_example-token"},"token_type":{"type":"string","const":"Bearer"}}},"Account":{"type":"object","required":["id","email"],"properties":{"id":{"type":"integer"},"email":{"type":"string","format":"email"},"name":{"type":["string","null"]}}},"Newsletter":{"type":"object","required":["id","name","slug","role"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":["string","null"]},"role":{"type":"string","enum":["owner","editor"]}}},"Bookmarks":{"type":"object","required":["newsletter_id","bookmarks"],"properties":{"newsletter_id":{"type":"string","format":"uuid"},"bookmarks":{"type":"string","description":"Bookmark content rendered as HTML."}}},"Issue":{"type":"object","required":["id","newsletter_id","status","content","summary","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"newsletter_id":{"type":"string","format":"uuid"},"subject":{"type":["string","null"]},"status":{"type":"string","enum":["draft","in_progress","sent"]},"content":{"type":"string","description":"Issue body as HTML."},"summary":{"type":"string","description":"Issue summary as HTML."},"archive":{"type":["boolean","null"]},"meta_title":{"type":["string","null"]},"meta_description":{"type":["string","null"]},"meta_keywords":{"type":["string","null"]},"twitter_site":{"type":["string","null"]},"twitter_creator":{"type":["string","null"]},"og_url":{"type":["string","null"]},"og_title":{"type":["string","null"]},"canonical":{"type":["string","null"]},"noindex":{"type":["boolean","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"IssueAttributes":{"type":"object","description":"At least one editable issue field should be provided when updating.","properties":{"subject":{"type":["string","null"]},"content":{"type":"string","description":"HTML issue body."},"summary":{"type":"string","description":"HTML issue summary."},"archive":{"type":"boolean"},"meta_title":{"type":["string","null"]},"meta_description":{"type":["string","null"]},"meta_keywords":{"type":["string","null"]},"twitter_site":{"type":["string","null"]},"twitter_creator":{"type":["string","null"]},"og_url":{"type":["string","null"]},"og_title":{"type":["string","null"]},"canonical":{"type":["string","null"]},"noindex":{"type":"boolean"}},"additionalProperties":false},"IssueRequestEnvelope":{"type":"object","required":["issue"],"properties":{"issue":{"$ref":"#/components/schemas/IssueAttributes"}},"additionalProperties":false},"Error":{"type":"object","required":["error","error_description"],"properties":{"error":{"type":"string"},"error_description":{"type":"string"}}},"ValidationError":{"type":"object","required":["error","errors"],"properties":{"error":{"type":"string","const":"validation_failed"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}}}