If you're building or maintaining a custom website for a WorkPress customer, this is everything you need to pull published project data and keep it in sync — no account manager required.
WorkPress is a headless CMS: it's where your customer manages and publishes project data. Your website is its own codebase and hosting — not generated or hosted by WorkPress — but it has no project data of its own. It depends entirely on WorkPress's API to fetch what the customer has published and render it in your own design.
There are two moving pieces:
| WorkPress Dashboard | Where the customer manages and publishes projects. You never need to touch this. |
| Your Website | Fetches published projects from the public API below, at build time or request time. |
The integration is three pieces, in order of how much you'll touch them:
| 1 | Fetch | Call the public API to list projects or fetch one by slug. It only ever returns published data for organizations with an active WorkPress account. |
| 2 | Render | Build pages from the response shape documented below — titles, photos, location, and SEO fields are already generated for you. |
| 3 | Stay in sync | Register a webhook endpoint so WorkPress can tell your site exactly what changed, instead of you polling or rebuilding on a timer. |
There's no API key or token on the request itself — endpoints under /api/public/ are called with just an organization ID. But every request is checked against that organization's account status on our side: endpoints only return data for organizations with an active WorkPress account. An org that hasn't been activated, or whose account has lapsed, gets a 403 on every public endpoint — including this one for a project that used to be live.
In practice this means: integrating against this API only works for a customer who is actually on WorkPress. There's nothing here you can stand up independently of a live account.
Webhook calls from WorkPress to your site are separately authenticated with a per-organization shared secret — see Webhooks below.
{orgId} below with the organization ID your customer shares with you from their dashboard settings.Returns every published project for an organization, newest first.
[
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Roof Replacement - Oak Lawn",
"slug": "roof-replacement-oak-lawn",
"seoTitle": "Roof Replacement in Dallas, TX",
"metaDescription": "Expert roof replacement services...",
"services": ["Roof Replacement", "Underlayment"],
"cover_photo_url": "https://...",
"address": { "city": "Dallas", "state": "TX" },
"is_published": true
}
]Returns a single published project by its SEO-optimized slug.
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Roof Replacement - Oak Lawn",
"slug": "roof-replacement-oak-lawn",
"seoTitle": "Roof Replacement in Dallas, TX",
"metaDescription": "Expert roof replacement services...",
"description": "Full roof replacement with new underlayment...",
"services": ["Roof Replacement", "Underlayment"],
"address": {
"street": "123 Main St",
"city": "Dallas",
"state": "TX",
"zip": "75201"
},
"coordinates": { "lat": 32.7767, "lon": -96.7970 },
"is_published": true,
"created_at": "2024-01-15T00:00:00.000Z"
}Returns the ordered photo gallery for a project.
[
{
"id": "photo_01",
"url": "https://media.workpressapp.com/...",
"thumbnail_url": "https://media.workpressapp.com/...",
"caption": "Finished roof, front elevation",
"order": 1,
"type": "before"
}
]type is "before" | "after" | "gallery" | null. Only render a before/after comparison slider if at least one photo of each type exists — otherwise show a normal gallery.Returns basic business info — useful for footers, schema markup, or contact blocks.
{
"company_name": "Acme Roofing",
"company_phone": "+1-214-555-0100",
"company_address": "123 Main St, Dallas, TX 75201",
"website_url": "https://acmeroofing.com"
}| Field | Type | Notes |
|---|---|---|
| slug | string | SEO-optimized, URL-safe identifier. Use this for routing, not id. |
| seoTitle | string | Pre-generated, location-aware page title. Use directly as your <title> / H1. |
| metaDescription | string | 55–160 characters, ready for your meta description tag. |
| services | string[] | 1–2 primary services performed, e.g. ["Roof Replacement"]. |
| address / coordinates | object | Street-level address and lat/lon for the job site. |
| is_published | boolean | Always true on public endpoints — included for convenience, not filtering. |
| Field | Type | Notes |
|---|---|---|
| url | string | Direct, publicly-hosted image URL. |
| thumbnail_url | string | Resized thumbnail version of the same image. |
| caption | string | Optional. May be empty. |
| order | number | Sort ascending for the order the customer arranged in their gallery. |
| type | string | null | "before", "after", "gallery", or null. |
Instead of polling, register an endpoint on your site. When a project is published or updated in the dashboard, WorkPress sends a POST request so you can revalidate just the affected page.
POST /your-revalidate-endpoint x-revalidate-token: <organization's revalidate_secret> Content-Type: application/json
{
"city": "dallas",
"slug": "roof-replacement"
}Your endpoint should check the x-revalidate-token header against the secret your customer copies from their dashboard's integration settings, then trigger revalidation (e.g. Next.js revalidatePath / ISR) for that project's page.
Confirm the project is Published, not a draft, that you're using the correct organization ID from the customer's dashboard, and that their WorkPress account is active. A 403 on every endpoint for an org that used to work usually means the account lapsed — contact the WorkPress team below.
If you're not using webhooks, you're likely serving a cached build — rebuild or revalidate on a shorter interval. If you are using webhooks, verify the endpoint URL and shared secret in the dashboard match what your site expects.
Stuck on an integration, or need an organization ID / webhook secret reissued? Reach out and we'll get you unblocked.
Email the WorkPress Team