API Getting Started Guide
Everything you need to integrate with the ActRight API: authentication, endpoints, webhooks, and error handling.
https://api.actright.comAuthentication
All API requests require a Bearer token in the Authorization header. There are two ways to authenticate:
Option A: API Key
Create an API key from your Dashboard → API Keys page. Keys are prefixed with gsk_ and can be scoped to read, read_write, or full access.
curl https://api.actright.com/v1/petitions \
-H "Authorization: Bearer gsk_your_api_key_here"Option B: OAuth 2.0 Token
For apps that act on behalf of users, register an OAuth App and use the authorization code flow to obtain a JWT access token.
curl https://api.actright.com/v1/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Security tip: Never commit API keys to source control. Use environment variables or a secrets manager. API keys with full scope can perform destructive actions.
Quick Start
List petitions
curl https://api.actright.com/v1/petitions?limit=5 \
-H "Authorization: Bearer gsk_your_key"{
"data": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"title": "Save the Local Park",
"slug": "save-the-local-park",
"signatureCount": 1248,
"signatureGoal": 5000,
"status": "active"
}
],
"meta": { "total": 42, "limit": 5, "hasMore": true }
}Sign a petition
curl -X POST https://api.actright.com/v1/petitions/save-the-local-park/signatures \
-H "Authorization: Bearer gsk_your_key" \
-H "Content-Type: application/json" \
-d '{
"comment": "I support this cause!",
"isAnonymous": false
}'{
"data": {
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"petitionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"comment": "I support this cause!",
"isAnonymous": false,
"createdAt": "2026-02-10T12:00:00Z"
}
}Create a petition
curl -X POST https://api.actright.com/v1/petitions \
-H "Authorization: Bearer gsk_your_key" \
-H "Content-Type: application/json" \
-d '{
"title": "Improve Public Transit",
"description": "<p>We need better bus routes in our city.</p>",
"categoryId": "uuid-of-category",
"signatureGoal": 1000,
"tags": ["transit", "infrastructure"]
}'{
"data": {
"id": "new-petition-uuid",
"title": "Improve Public Transit",
"slug": "improve-public-transit",
"status": "draft",
"createdAt": "2026-02-10T12:00:00Z"
}
}Petitions are created in draft status. Use POST /v1/petitions/:slug/publish to make them publicly visible.
Rate Limiting
API requests are rate limited per API key or access token to ensure fair usage for all developers.
Rate limit headers
Every response includes these headers so you can track your usage:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the window |
X-RateLimit-Reset | Unix timestamp when the window resets |
{
"type": "https://api.groundswell.io/errors/rate-limited",
"title": "Rate limit exceeded",
"status": 429,
"detail": "You have exceeded 50 requests per second. Retry after 1.2s.",
"instance": "/v1/petitions"
}API Versioning
The ActRight API uses URL-based versioning. All endpoints are prefixed with the version number (e.g. /v1/).
Current version
The current API version is v1. Every response includes a version header:
X-API-Version: 1Versioning strategy
| Aspect | Policy |
|---|---|
| Scheme | URL path prefix (/v1/, /v2/, etc.) |
| Deprecation | Each major version is supported for 12 months after the next version is released |
| Breaking changes | Communicated via the developer changelog and deprecation headers |
| Additive changes | New fields and endpoints may be added to an existing version without a version bump |
Tip: Always pin your integration to a specific version (e.g. /v1/). When a new version is released, test your integration against it before migrating.
Embed Widget
Embed any ActRight petition on your website using an iframe or the oEmbed protocol. The widget is fully responsive and supports theming.
iframe embed
Copy and paste the following HTML to embed a petition:
<iframe
src="https://groundswell.io/embed/p/{slug}"
width="100%"
height="400"
frameborder="0"
></iframe>Replace {slug} with the petition slug (e.g. save-the-local-park).
oEmbed endpoint
Use oEmbed for CMS platforms and rich-preview tools:
GET https://api.actright.com/v1/oembed?url=https://groundswell.io/p/{slug}&format=jsonTheme support
The embed widget defaults to light mode. Append ?theme=dark for dark mode:
<iframe
src="https://groundswell.io/embed/p/{slug}?theme=dark"
width="100%"
height="400"
frameborder="0"
></iframe>Responsive behavior
The widget automatically resizes its height via postMessage. To enable auto-resizing, add this listener on your page:
window.addEventListener("message", (event) => {
if (event.data?.type === "actright:resize") {
const iframe = document.querySelector("iframe[src*='groundswell.io']");
if (iframe) iframe.style.height = event.data.height + "px";
}
});WordPress: For WordPress sites, use the official ActRight plugin for a one-click embed experience. View WordPress docs
Webhook Events
Register webhook endpoints from your Dashboard → Webhooks page or via the API. ActRight will send POST requests to your URL for each subscribed event.
Event types
| Event | Description |
|---|---|
petition.created | A new petition has been created (draft). |
petition.updated | Petition title, description, or settings were updated. |
petition.published | A petition was published and is now publicly visible. |
petition.closed | A petition was closed by its creator or an admin. |
petition.victory | A petition was marked as victorious. |
signature.created | Someone signed a petition. |
signature.milestone | A signature milestone was reached (e.g. 100, 500, 1000 signatures). |
signature.removed | A signature was removed by the signer or an admin. |
update.created | The petition creator posted an update. |
payment.completed | A payment (promotion or boost) was completed. |
response.submitted | A decision maker submitted an official response. |
response.published | A decision maker response was published. |
Delivery headers
Each webhook delivery includes these headers:
| Header | Description |
|---|---|
X-ActRight-Signature | HMAC-SHA256 signature: sha256={hex} |
X-ActRight-Event | The event type (e.g. signature.created) |
X-ActRight-Delivery-Id | Unique delivery UUID for deduplication |
X-ActRight-Timestamp | ISO 8601 timestamp of the event |
Verifying signatures
import crypto from "crypto";
function verifyWebhook(payload, signature, secret) {
const expected = "sha256=" +
crypto.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Retry policy: Failed deliveries are retried up to 5 times with exponential backoff. Your endpoint must respond with a 2xx status within 10 seconds to be considered successful.
Error Format
All error responses follow the RFC 7807 Problem Details specification. Every error includes a machine-readable type URI, a human-readable title, and the HTTP status code.
| Field | Type | Description |
|---|---|---|
type | string | URI identifying the error type |
title | string | Short human-readable summary |
status | number | HTTP status code |
detail | string | Detailed explanation of the error |
instance | string | Request path that generated the error |
errors | array? | Field-level validation errors (422 responses) |
Validation error (422)
{
"type": "https://api.groundswell.io/errors/validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "One or more fields are invalid.",
"instance": "/v1/petitions",
"errors": [
{ "field": "title", "message": "Required", "code": "invalid_type" },
{ "field": "description", "message": "String must contain at least 10 character(s)", "code": "too_small" }
]
}Not found (404)
{
"type": "https://api.groundswell.io/errors/not-found",
"title": "Not found",
"status": 404,
"detail": "Petition with slug 'nonexistent' was not found.",
"instance": "/v1/petitions/nonexistent"
}Authentication error (401)
{
"type": "https://api.groundswell.io/errors/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Missing or invalid Bearer token.",
"instance": "/v1/petitions"
}API Playground
Try out API endpoints directly from this page. Enter your API key, select an endpoint, and send a request.
API Playground
LiveReady to build?
Get an API key and start making requests in minutes.