{"openapi":"3.0.3","info":{"title":"ActRight API","description":"The ActRight API provides programmatic access to the petition platform.\n\n## Authentication\n\nThe API supports two authentication methods:\n\n### Bearer Token (JWT)\nFor user-authenticated requests, include a JWT token in the Authorization header:\n```\nAuthorization: Bearer <token>\n```\n\n### API Key\nFor server-to-server integrations, use an API key in the X-API-Key header:\n```\nX-API-Key: ar_live_<key>\n```\n\n## Rate Limiting\n\nRate limit headers are included in all responses:\n- `X-RateLimit-Limit`: Maximum requests allowed\n- `X-RateLimit-Remaining`: Requests remaining in current window\n- `X-RateLimit-Reset`: Unix timestamp when the limit resets\n\n## Errors\n\nThe API uses RFC 7807 Problem Details for error responses:\n```json\n{\n  \"type\": \"https://api.groundswell.io/errors/validation-failed\",\n  \"title\": \"Validation Failed\",\n  \"status\": 422,\n  \"detail\": \"Request validation failed\",\n  \"instance\": \"/v1/petitions\",\n  \"errors\": [...]\n}\n```","version":"1.0.0","contact":{"name":"ActRight API Support","email":"api-support@groundswell.io","url":"https://docs.groundswell.io"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"{baseUrl}","description":"Configurable base URL","variables":{"baseUrl":{"default":"http://localhost:3000","description":"API server base URL"}}},{"url":"https://api.groundswell.io","description":"Production"},{"url":"https://api.staging.groundswell.io","description":"Staging"}],"tags":[{"name":"Authentication","description":"Login, token refresh, logout, and token revocation"},{"name":"Petitions","description":"Create, read, update, delete, and manage petition lifecycle"},{"name":"Signatures","description":"Sign petitions, list and manage signatures"},{"name":"Comments","description":"Add and manage comments on petitions"},{"name":"Users","description":"User profile management"},{"name":"Search","description":"Full-text search and autocomplete for petitions"},{"name":"Organizations","description":"Organization management and memberships"},{"name":"Media","description":"Upload and manage media files via pre-signed URLs"},{"name":"Notifications","description":"User notification management and preferences"},{"name":"API Keys","description":"Manage API keys for programmatic access"},{"name":"Webhooks","description":"Configure webhook endpoints for event notifications"},{"name":"OAuth","description":"OAuth 2.0 authorization and token endpoints"},{"name":"Analytics","description":"Petition analytics and platform statistics"},{"name":"Updates","description":"Petition updates posted by creators"},{"name":"Respond","description":"Decision maker response portal (token-based, no JWT)"},{"name":"Reports","description":"Content reporting for moderation"},{"name":"oEmbed","description":"oEmbed endpoint for embedding petitions"},{"name":"Admin","description":"Administrative endpoints for platform moderation"}],"paths":{"/v1/auth/login":{"post":{"tags":["Authentication"],"summary":"Admin login","description":"Authenticate with email and password via Cognito. Returns JWT tokens for API access.","operationId":"login","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":1}}}}}},"responses":{"200":{"description":"Authentication successful","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"ID token (JWT)"},"access_token":{"type":"string"},"refresh_token":{"type":"string"},"expires_in":{"type":"integer","example":3600},"token_type":{"type":"string","example":"Bearer"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh access token","description":"Exchange a valid refresh token for a new access token and refresh token pair.","operationId":"refreshToken","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string","minLength":1}}}}}},"responses":{"200":{"description":"Token refreshed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataEnvelope"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/auth/logout":{"post":{"tags":["Authentication"],"summary":"Log out","description":"Revoke a refresh token to end the user session.","operationId":"logout","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string","minLength":1}}}}}},"responses":{"204":{"description":"Token revoked successfully"}}}},"/v1/auth/revoke":{"post":{"tags":["Authentication"],"summary":"Revoke a token","description":"Blacklist an access token or revoke a refresh token for immediate invalidation.","operationId":"revokeToken","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","minLength":1},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"]}}}}}},"responses":{"204":{"description":"Token revoked successfully"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/petitions":{"get":{"tags":["Petitions"],"summary":"List petitions","description":"List petitions with optional filtering by status, category, creator, and tags. Supports cursor-based pagination.","operationId":"listPetitions","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["draft","active","closed","victory"]}},{"name":"categoryId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"creatorId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"tags","in":"query","description":"Comma-separated list of tags","schema":{"type":"string"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["recent","popular","trending"],"default":"recent"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"List of petitions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedPetitions"}}}}}},"post":{"tags":["Petitions"],"summary":"Create a petition","description":"Create a new petition in draft status. Requires authentication.","operationId":"createPetition","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionCreate"}}}},"responses":{"201":{"description":"Petition created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionEnvelope"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/petitions/export":{"get":{"tags":["Petitions"],"summary":"Export petition data","description":"Export petition data in CSV or JSON format. Supports filtering by status, category, and creator. Rate limited to 10 requests per minute.","operationId":"exportPetitions","security":[{"bearerAuth":[]}],"parameters":[{"name":"format","in":"query","schema":{"type":"string","enum":["csv","json"],"default":"json"}},{"name":"status","in":"query","schema":{"type":"string","enum":["draft","active","closed","victory"]}},{"name":"categoryId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"creatorId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"includeSignatures","in":"query","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/Cursor"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}}],"responses":{"200":{"description":"Exported data in requested format"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/petitions/{slug}":{"get":{"tags":["Petitions"],"summary":"Get petition by slug","description":"Retrieve a petition by its URL slug. Private petitions require creator auth or valid access code.","operationId":"getPetition","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"access_code","in":"query","description":"Access code for private petitions","schema":{"type":"string"}}],"responses":{"200":{"description":"Petition details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionEnvelope"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/petitions/{id}":{"patch":{"tags":["Petitions"],"summary":"Update a petition","description":"Update petition fields. Only the petition owner may perform this action.","operationId":"updatePetition","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionUpdate"}}}},"responses":{"200":{"description":"Petition updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionEnvelope"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"tags":["Petitions"],"summary":"Delete a petition","description":"Soft-delete a petition. Only the petition owner may perform this action.","operationId":"deletePetition","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"204":{"description":"Petition deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/petitions/{id}/publish":{"post":{"tags":["Petitions"],"summary":"Publish a petition","description":"Transition a petition from draft to active status. Sets the published_at timestamp.","operationId":"publishPetition","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Petition published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionEnvelope"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/v1/petitions/{id}/close":{"post":{"tags":["Petitions"],"summary":"Close a petition","description":"Transition a petition from active to closed status.","operationId":"closePetition","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Petition closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionEnvelope"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/v1/petitions/{id}/victory":{"post":{"tags":["Petitions"],"summary":"Declare victory on a petition","description":"Transition a petition from active to victory status. Optionally accepts a victory message.","operationId":"declareVictory","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"victory_message":{"type":"string","maxLength":10000}}}}}},"responses":{"200":{"description":"Victory declared","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PetitionEnvelope"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/v1/petitions/{id}/access":{"post":{"tags":["Petitions"],"summary":"Validate petition access code","description":"Validate an access code for a private petition.","operationId":"validateAccessCode","parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accessCode"],"properties":{"accessCode":{"type":"string"}}}}}},"responses":{"200":{"description":"Access code valid","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"valid":{"type":"boolean"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/petitions/{slug}/leaderboard":{"get":{"tags":["Petitions"],"summary":"Get petition sharing leaderboard","description":"Retrieve the top sharers for a petition with their share counts. If authenticated, also returns the current user's rank.","operationId":"getLeaderboard","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}}],"responses":{"200":{"description":"Leaderboard data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"topSharers":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"displayName":{"type":"string"},"slug":{"type":"string"},"avatarUrl":{"type":"string","nullable":true},"shareCount":{"type":"integer"}}}},"totalShares":{"type":"integer"},"currentUserRank":{"type":"integer"},"currentUserShareCount":{"type":"integer"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/petitions/{id}/signatures":{"get":{"tags":["Signatures"],"summary":"List signatures for a petition","description":"List signatures for a petition with cursor pagination. Anonymous signatures return user: null.","operationId":"listSignatures","parameters":[{"$ref":"#/components/parameters/IdPath"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"List of signatures","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSignatures"}}}},"403":{"$ref":"#/components/responses/Forbidden"}}},"post":{"tags":["Signatures"],"summary":"Sign a petition","description":"Sign a petition. Requires authentication. Rate-limited by IP address (10/hour).","operationId":"signPetition","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignatureCreate"}}}},"responses":{"201":{"description":"Petition signed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"petition_id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"is_anonymous":{"type":"boolean"},"signature_count":{"type":"integer"},"milestone_reached":{"type":"boolean"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/petitions/{id}/signatures/me":{"delete":{"tags":["Signatures"],"summary":"Remove your signature","description":"Remove the authenticated user's signature from a petition.","operationId":"unsignPetition","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"204":{"description":"Signature removed"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/petitions/{id}/signatures/geo":{"get":{"tags":["Signatures"],"summary":"Get geographic signature data","description":"Returns geographic signature data for globe visualization. Public endpoint.","operationId":"getGeoSignatures","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Petition UUID or 'global'"}},{"name":"hours","in":"query","description":"Time window in hours (default 24, max 168)","schema":{"type":"integer","default":24,"maximum":168}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Geographic signature data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"},"city":{"type":"string"},"region":{"type":"string"},"country":{"type":"string"},"count":{"type":"integer"}}}},"pagination":{"$ref":"#/components/schemas/Pagination"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/petitions/{id}/comments":{"get":{"tags":["Comments"],"summary":"List comments on a petition","description":"Retrieve a paginated, threaded list of comments for a petition.","operationId":"listComments","parameters":[{"$ref":"#/components/parameters/IdPath"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"name":"sort","in":"query","schema":{"type":"string","enum":["recent","popular"],"default":"recent"}}],"responses":{"200":{"description":"Threaded comment list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Comment"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}}}},"post":{"tags":["Comments"],"summary":"Create a comment","description":"Create a new comment on a petition. Supports threaded replies via parent_id.","operationId":"createComment","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentCreate"}}}},"responses":{"201":{"description":"Comment created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Comment"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/comments/{id}":{"get":{"tags":["Comments"],"summary":"Get comment by ID","operationId":"getComment","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Comment details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Comment"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Comments"],"summary":"Update a comment","operationId":"updateComment","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"description":"Comment updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Comments"],"summary":"Delete a comment","operationId":"deleteComment","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"204":{"description":"Comment deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/users/me":{"get":{"tags":["Users"],"summary":"Get current user","description":"Get the authenticated user's full profile.","operationId":"getCurrentUser","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User profile","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/User"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Users"],"summary":"Update current user","description":"Update the authenticated user's profile information.","operationId":"updateCurrentUser","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}}},"responses":{"200":{"description":"Profile updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/users/{slug}":{"get":{"tags":["Users"],"summary":"Get public user profile","description":"Retrieve a public user profile by slug.","operationId":"getUserProfile","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Public profile","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PublicProfile"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/users/{slug}/petitions":{"get":{"tags":["Users"],"summary":"List user's petitions","operationId":"getUserPetitions","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"User's petitions list"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/users/{slug}/signatures":{"get":{"tags":["Users"],"summary":"List user's signatures","operationId":"getUserSignatures","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"User's signatures list"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/search/petitions":{"get":{"tags":["Search"],"summary":"Search petitions","description":"Full-text search for petitions with faceted filtering.","operationId":"searchPetitions","parameters":[{"name":"q","in":"query","description":"Search query string","schema":{"type":"string"}},{"name":"category","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"country","in":"query","description":"ISO 3166-1 alpha-2 country code","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["active","closed","victory"]}},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["relevance","recent","signatures","trending"],"default":"relevance"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PetitionSummary"}},"facets":{"type":"object"},"pagination":{"$ref":"#/components/schemas/Pagination"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"503":{"description":"Search service unavailable"}}}},"/v1/search/suggest":{"get":{"tags":["Search"],"summary":"Autocomplete suggestions","description":"Autocomplete suggestions for petition titles based on prefix search.","operationId":"suggestPetitions","parameters":[{"name":"q","in":"query","required":true,"description":"Search prefix (min 2 characters)","schema":{"type":"string","minLength":2}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":10,"default":5}}],"responses":{"200":{"description":"Suggestion results","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"slug":{"type":"string"},"signature_count":{"type":"integer"}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}}}}},"/v1/organizations":{"get":{"tags":["Organizations"],"summary":"List organizations","description":"Retrieve a paginated list of organizations.","operationId":"listOrganizations","parameters":[{"name":"verified_only","in":"query","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"List of organizations"}}},"post":{"tags":["Organizations"],"summary":"Create an organization","description":"Create a new organization. The authenticated user becomes the owner.","operationId":"createOrganization","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationCreate"}}}},"responses":{"201":{"description":"Organization created"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/v1/organizations/{slug}":{"get":{"tags":["Organizations"],"summary":"Get organization by slug","description":"Retrieve the public profile of an organization by its slug.","operationId":"getOrganization","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Organization profile"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/organizations/{org_id}":{"patch":{"tags":["Organizations"],"summary":"Update organization","operationId":"updateOrganization","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Organization updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Organizations"],"summary":"Delete organization","operationId":"deleteOrganization","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Organization deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/organizations/{org_id}/members":{"get":{"tags":["Organizations"],"summary":"List organization members","operationId":"listOrgMembers","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"role","in":"query","schema":{"type":"string","enum":["owner","admin","member"]}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"List of members"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Organizations"],"summary":"Add a member","description":"Add a new member to an organization. Requires admin or owner role.","operationId":"addOrgMember","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id","role"],"properties":{"user_id":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["admin","member"]}}}}}},"responses":{"201":{"description":"Member added"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Tier member limit reached"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/v1/organizations/{org_id}/members/{membership_id}":{"patch":{"tags":["Organizations"],"summary":"Update member role","operationId":"updateOrgMemberRole","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"membership_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["admin","member"]}}}}}},"responses":{"200":{"description":"Role updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Organizations"],"summary":"Remove a member","description":"Remove a member from an organization. Cannot remove the sole owner.","operationId":"removeOrgMember","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"membership_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Member removed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/organizations/{org_id}/members/{membership_id}/transfer-ownership":{"post":{"tags":["Organizations"],"summary":"Transfer organization ownership","description":"Transfer ownership of the organization to another admin member. Only the current owner can perform this action.","operationId":"transferOwnership","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"membership_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Ownership transferred"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/organizations/{org_id}/petitions":{"get":{"tags":["Organizations"],"summary":"List organization petitions","operationId":"listOrgPetitions","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Organization petitions"}}}},"/v1/organizations/{org_id}/subscription":{"get":{"tags":["Organizations"],"summary":"Get subscription status","operationId":"getOrgSubscription","security":[{"bearerAuth":[]}],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Subscription status"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/media/upload-url":{"post":{"tags":["Media"],"summary":"Get pre-signed upload URL","description":"Get a pre-signed S3 URL for direct upload. URL valid for 15 minutes.","operationId":"getUploadUrl","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["entity_type","filename","content_type"],"properties":{"entity_type":{"type":"string","enum":["petition_image","petition_update_image","user_avatar","org_logo","org_cover"]},"filename":{"type":"string","minLength":1,"maxLength":255},"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","image/gif","image/avif"]}}}}}},"responses":{"201":{"description":"Upload URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"media_id":{"type":"string","format":"uuid"},"upload_url":{"type":"string","format":"uri"},"expires_at":{"type":"string","format":"date-time"},"max_size_bytes":{"type":"integer","example":5242880}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/media/confirm":{"post":{"tags":["Media"],"summary":"Confirm media upload","description":"Confirm that an upload completed and trigger image processing.","operationId":"confirmUpload","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["media_id"],"properties":{"media_id":{"type":"string","format":"uuid"},"entity_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Upload confirmed and processing started"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"description":"File exceeds maximum size"}}}},"/v1/notifications":{"get":{"tags":["Notifications"],"summary":"List notifications","description":"Retrieve a paginated list of notifications for the authenticated user.","operationId":"listNotifications","security":[{"bearerAuth":[]}],"parameters":[{"name":"unread_only","in":"query","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Notifications list with counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"counts":{"type":"object","properties":{"total":{"type":"integer"},"unread":{"type":"integer"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/notifications/unread-count":{"get":{"tags":["Notifications"],"summary":"Get unread notification count","operationId":"getUnreadCount","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Unread count","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"unread_count":{"type":"integer"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/notifications/{id}":{"get":{"tags":["Notifications"],"summary":"Get a notification","operationId":"getNotification","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Notification details"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/notifications/mark-read":{"post":{"tags":["Notifications"],"summary":"Mark notifications as read","operationId":"markNotificationsRead","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"notification_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"mark_all":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Notifications marked as read"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/notifications/preferences":{"get":{"tags":["Notifications"],"summary":"Get notification preferences","operationId":"getNotificationPreferences","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Notification preferences"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"patch":{"tags":["Notifications"],"summary":"Update notification preferences","operationId":"updateNotificationPreferences","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Preferences updated"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/api-keys":{"get":{"tags":["API Keys"],"summary":"List API keys","description":"List all API keys for the authenticated user.","operationId":"listApiKeys","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of API keys"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["API Keys"],"summary":"Create API key","description":"Create a new API key. Returns the raw key once; it cannot be retrieved again.","operationId":"createApiKey","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"scopes":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"201":{"description":"API key created (includes raw key)"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/api-keys/{id}":{"get":{"tags":["API Keys"],"summary":"Get API key","operationId":"getApiKey","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"API key details"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["API Keys"],"summary":"Update API key","description":"Update an API key's name or scopes.","operationId":"updateApiKey","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"API key updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["API Keys"],"summary":"Revoke API key","description":"Permanently revoke an API key. This action cannot be undone.","operationId":"revokeApiKey","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"204":{"description":"API key revoked"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/api-keys/{id}/rotate":{"post":{"tags":["API Keys"],"summary":"Rotate API key","description":"Rotate an API key, generating a new key with a 24-hour grace period for the old key.","operationId":"rotateApiKey","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"API key rotated"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhook endpoints","description":"List all webhook endpoints registered by the authenticated user.","operationId":"listWebhooks","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of webhook endpoints","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Webhooks"],"summary":"Register webhook endpoint","description":"Register a new webhook endpoint. Returns the webhook secret for signature verification.","operationId":"createWebhook","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreate"}}}},"responses":{"201":{"description":"Webhook endpoint created (includes secret)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/webhooks/{id}":{"get":{"tags":["Webhooks"],"summary":"Get webhook endpoint","operationId":"getWebhook","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Webhook endpoint details"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Webhooks"],"summary":"Update webhook endpoint","operationId":"updateWebhook","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdate"}}}},"responses":{"200":{"description":"Webhook endpoint updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Webhooks"],"summary":"Delete webhook endpoint","operationId":"deleteWebhook","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"204":{"description":"Webhook endpoint deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List webhook deliveries","description":"List delivery history for a specific webhook endpoint.","operationId":"listWebhookDeliveries","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","delivered","failed","retrying"]}},{"$ref":"#/components/parameters/Cursor"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Delivery history","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/oauth/authorize":{"get":{"tags":["OAuth"],"summary":"OAuth 2.0 authorization","description":"OAuth 2.0 authorization endpoint. Shows a consent screen after validating client and redirect_uri.","operationId":"oauthAuthorize","security":[{"bearerAuth":[]}],"parameters":[{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri"}},{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"state","in":"query","required":true,"schema":{"type":"string"}},{"name":"scope","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Consent screen HTML"},"302":{"description":"Redirect with authorization code or error"},"400":{"description":"Invalid request parameters"}}}},"/oauth/token":{"post":{"tags":["OAuth"],"summary":"OAuth 2.0 token exchange","description":"Exchange authorization code, refresh token, or client credentials for an access token.","operationId":"oauthToken","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token","client_credentials"]},"code":{"type":"string","description":"Authorization code (for authorization_code grant)"},"redirect_uri":{"type":"string","description":"Must match the original redirect_uri"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"refresh_token":{"type":"string","description":"For refresh_token grant"}}}}}},"responses":{"200":{"description":"Token response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"integer"},"refresh_token":{"type":"string"},"scope":{"type":"string"}}}}}},"400":{"description":"Invalid grant or request"}}}},"/v1/petitions/{id}/analytics":{"get":{"tags":["Analytics"],"summary":"Get petition analytics","description":"Returns analytics data for a petition including signature growth, geographic breakdown, and traffic sources. Requires creator or org admin permission.","operationId":"getPetitionAnalytics","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"start_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"end_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"granularity","in":"query","schema":{"type":"string","enum":["day","week","month"]}}],"responses":{"200":{"description":"Analytics data"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/analytics/stats":{"get":{"tags":["Analytics"],"summary":"Get platform analytics stats","description":"Returns aggregated analytics from Snowflake dbt models including daily stats, trending scores, and geographic distribution.","operationId":"getAnalyticsStats","parameters":[{"name":"petition_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"start_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"end_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}}],"responses":{"200":{"description":"Analytics statistics"},"500":{"description":"Analytics query failed"}}}},"/respond/{token}":{"get":{"tags":["Respond"],"summary":"Load response portal","description":"Load decision maker response portal context. Token-based auth (no JWT required).","operationId":"getResponsePortal","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Portal context loaded"},"404":{"description":"Token not found"},"410":{"description":"Token expired"}}},"post":{"tags":["Respond"],"summary":"Submit a response","description":"Submit a decision maker response. Token-based auth (no JWT required).","operationId":"submitResponse","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string"},"status":{"type":"string","enum":["acknowledged","supportive","opposed","neutral"]}}}}}},"responses":{"201":{"description":"Response submitted"},"404":{"description":"Token not found"},"409":{"description":"Already responded"},"410":{"description":"Token expired"}}}},"/v1/oembed":{"get":{"tags":["oEmbed"],"summary":"oEmbed endpoint","description":"Returns oEmbed JSON response for embedding petitions on external sites. Spec: https://oembed.com/","operationId":"getOEmbed","parameters":[{"name":"url","in":"query","required":true,"description":"URL of the petition to embed","schema":{"type":"string","format":"uri"}},{"name":"maxwidth","in":"query","schema":{"type":"integer"}},{"name":"maxheight","in":"query","schema":{"type":"integer"}},{"name":"format","in":"query","schema":{"type":"string","enum":["json","xml"],"default":"json"}}],"responses":{"200":{"description":"oEmbed response","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","example":"rich"},"version":{"type":"string","example":"1.0"},"title":{"type":"string"},"provider_name":{"type":"string","example":"ActRight"},"provider_url":{"type":"string"},"html":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"cache_age":{"type":"integer"},"thumbnail_url":{"type":"string"},"thumbnail_width":{"type":"integer"},"thumbnail_height":{"type":"integer"}}}}}},"400":{"description":"Invalid request"},"404":{"$ref":"#/components/responses/NotFound"},"501":{"description":"XML format not supported"}}}},"/v1/reports":{"post":{"tags":["Reports"],"summary":"Create a content report","description":"Submit a content report for moderation review.","operationId":"createReport","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["target_type","target_id","reason"],"properties":{"target_type":{"type":"string","enum":["petition","comment","user"]},"target_id":{"type":"string","format":"uuid"},"reason":{"type":"string","enum":["spam","harassment","hate_speech","misinformation","other"]},"description":{"type":"string","maxLength":1000}}}}}},"responses":{"201":{"description":"Report submitted"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/admin/reports":{"get":{"tags":["Admin"],"summary":"List moderation reports","operationId":"adminListReports","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of reports"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/reports/{id}/action":{"post":{"tags":["Admin"],"summary":"Take action on a report","operationId":"adminActionReport","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Action taken"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/users":{"get":{"tags":["Admin"],"summary":"Search users","operationId":"adminSearchUsers","security":[{"bearerAuth":[]}],"parameters":[{"name":"email","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"User search results"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/users/{id}/suspend":{"post":{"tags":["Admin"],"summary":"Suspend user","operationId":"adminSuspendUser","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"User suspended"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/users/{id}/unsuspend":{"post":{"tags":["Admin"],"summary":"Unsuspend user","operationId":"adminUnsuspendUser","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"User unsuspended"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/metrics":{"get":{"tags":["Admin"],"summary":"Get platform metrics","operationId":"adminGetMetrics","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Platform metrics dashboard"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/audit-logs":{"get":{"tags":["Admin"],"summary":"Query audit logs","operationId":"adminListAuditLogs","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Audit log entries"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/warnings":{"get":{"tags":["Admin"],"summary":"List warnings","operationId":"adminListWarnings","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of warnings"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"post":{"tags":["Admin"],"summary":"Issue a warning","operationId":"adminCreateWarning","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Warning issued"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/comments/{id}/hide":{"post":{"tags":["Admin"],"summary":"Hide a comment","operationId":"adminHideComment","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Comment hidden"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/admin/comments/{id}/unhide":{"post":{"tags":["Admin"],"summary":"Unhide a comment","operationId":"adminUnhideComment","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Comment unhidden"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/health":{"get":{"summary":"Health check","operationId":"healthCheck","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token obtained from Cognito authentication"},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for server-to-server integrations. Format: ar_live_<key> or ar_test_<key>"},"oauth2":{"type":"oauth2","flows":{"authorizationCode":{"authorizationUrl":"/oauth/authorize","tokenUrl":"/oauth/token","scopes":{"read:petitions":"Read petition data","write:petitions":"Create and update petitions","read:signatures":"Read signature data","write:signatures":"Create signatures","read:profile":"Read user profile","write:profile":"Update user profile"}}}}},"parameters":{"IdPath":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"Cursor":{"name":"cursor","in":"query","description":"Opaque cursor for pagination. Use the next_cursor value from a previous response.","schema":{"type":"string"}},"Limit":{"name":"limit","in":"query","description":"Maximum number of items to return","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}},"schemas":{"Meta":{"type":"object","properties":{"request_id":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"Pagination":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true},"has_more":{"type":"boolean"}}},"DataEnvelope":{"type":"object","properties":{"data":{"type":"object"},"meta":{"$ref":"#/components/schemas/Meta"}}},"ErrorEnvelope":{"type":"object","required":["type","title","status","detail","instance"],"properties":{"type":{"type":"string","example":"https://api.groundswell.io/errors/validation-failed"},"title":{"type":"string","example":"Validation Failed"},"status":{"type":"integer","example":422},"detail":{"type":"string"},"instance":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"},"code":{"type":"string"}}}}}},"PetitionCreate":{"type":"object","required":["title","description"],"properties":{"title":{"type":"string","minLength":5,"maxLength":200},"description":{"type":"string","minLength":50,"maxLength":50000},"categoryId":{"type":"string","format":"uuid"},"tags":{"type":"array","items":{"type":"string"},"maxItems":10},"imageUrl":{"type":"string","format":"uri"},"signatureGoal":{"type":"integer","minimum":1},"visibility":{"type":"string","enum":["public","private","unlisted"],"default":"public"},"locationCity":{"type":"string"},"locationRegion":{"type":"string"},"locationCountry":{"type":"string"},"signaturePrivacy":{"type":"string","enum":["public","creator_only","count_only"]},"decisionMakers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"title":{"type":"string"},"email":{"type":"string","format":"email"}}}},"orgId":{"type":"string","format":"uuid"}}},"PetitionUpdate":{"type":"object","properties":{"title":{"type":"string","minLength":5,"maxLength":200},"description":{"type":"string","minLength":50,"maxLength":50000},"categoryId":{"type":"string","format":"uuid"},"tags":{"type":"array","items":{"type":"string"}},"imageUrl":{"type":"string","format":"uri"},"signatureGoal":{"type":"integer","minimum":1},"visibility":{"type":"string","enum":["public","private","unlisted"]}}},"PetitionSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["draft","active","closed","victory"]},"signature_count":{"type":"integer"},"signature_goal":{"type":"integer","nullable":true},"image_url":{"type":"string","nullable":true},"category":{"type":"object","nullable":true},"creator":{"$ref":"#/components/schemas/PublicProfile"},"created_at":{"type":"string","format":"date-time"},"published_at":{"type":"string","format":"date-time","nullable":true}}},"PetitionEnvelope":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PetitionSummary"},"meta":{"$ref":"#/components/schemas/Meta"}}},"PaginatedPetitions":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PetitionSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"meta":{"$ref":"#/components/schemas/Meta"}}},"SignatureCreate":{"type":"object","properties":{"comment":{"type":"string","maxLength":500},"is_anonymous":{"type":"boolean","default":false},"display_location":{"type":"string"},"recaptchaToken":{"type":"string"}}},"PaginatedSignatures":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"user":{"$ref":"#/components/schemas/PublicProfile"},"comment":{"type":"string","nullable":true},"is_anonymous":{"type":"boolean"},"display_location":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true},"has_more":{"type":"boolean"},"total_count":{"type":"integer"}}},"meta":{"$ref":"#/components/schemas/Meta"}}},"Comment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"petition_id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"parent_id":{"type":"string","format":"uuid","nullable":true},"body":{"type":"string"},"is_hidden":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"replies":{"type":"array","items":{"$ref":"#/components/schemas/Comment"}}}},"CommentCreate":{"type":"object","required":["body"],"properties":{"body":{"type":"string","minLength":1,"maxLength":2000},"parent_id":{"type":"string","format":"uuid","description":"ID of parent comment for threaded replies"}}},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"display_name":{"type":"string"},"slug":{"type":"string"},"bio":{"type":"string","nullable":true},"avatar_url":{"type":"string","nullable":true},"email_verified":{"type":"boolean"},"phone_verified":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}},"UserUpdate":{"type":"object","properties":{"display_name":{"type":"string","maxLength":100},"bio":{"type":"string","maxLength":500},"avatar_url":{"type":"string","format":"uri"},"notification_prefs":{"type":"object"}}},"PublicProfile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"display_name":{"type":"string"},"slug":{"type":"string"},"bio":{"type":"string","nullable":true},"avatar_url":{"type":"string","nullable":true},"petition_count":{"type":"integer"}}},"OrganizationCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":2,"maxLength":100},"description":{"type":"string","maxLength":1000},"logo_url":{"type":"string","format":"uri"},"website":{"type":"string","format":"uri"}}},"Notification":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"read":{"type":"boolean"},"data":{"type":"object"},"created_at":{"type":"string","format":"date-time"}}},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}},"WebhookEndpointWithSecret":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","properties":{"secret":{"type":"string","description":"Webhook signing secret. Store securely; only shown once."}}}]},"WebhookCreate":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["petition.created","petition.published","petition.updated","petition.closed","petition.victory","signature.created","signature.removed","signature.milestone","comment.created","comment.updated","comment.deleted","update.created"]},"minItems":1}}},"WebhookUpdate":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean"},"regenerate_secret":{"type":"boolean"}}},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed","retrying"]},"response_status":{"type":"integer","nullable":true},"attempt":{"type":"integer"},"delivered_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}}},"responses":{"Unauthorized":{"description":"Authentication required or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"type":"https://api.groundswell.io/errors/authentication-failed","title":"Authentication Failed","status":401,"detail":"Invalid or expired token","instance":"/v1/petitions"}}}},"Forbidden":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"type":"https://api.groundswell.io/errors/forbidden","title":"Forbidden","status":403,"detail":"You do not have permission to perform this action","instance":"/v1/petitions/123"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"type":"https://api.groundswell.io/errors/not-found","title":"Not Found","status":404,"detail":"Resource not found","instance":"/v1/petitions/nonexistent"}}}},"Conflict":{"description":"Conflict (e.g., duplicate resource or invalid state transition)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"type":"https://api.groundswell.io/errors/invalid-state-transition","title":"Invalid State Transition","status":409,"detail":"Cannot transition from 'closed' to 'active'","instance":"/v1/petitions/123/publish"}}}},"ValidationError":{"description":"Request validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"type":"https://api.groundswell.io/errors/validation-failed","title":"Validation Failed","status":422,"detail":"Field validation error","instance":"/v1/petitions","errors":[{"field":"title","message":"Title is required","code":"required"}]}}}},"RateLimited":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"type":"https://api.groundswell.io/errors/rate-limited","title":"Rate Limited","status":429,"detail":"Too many requests. Please try again later.","instance":"/v1/auth/login"}}}}}}}