Repository navigation
Omerlo API
PULSE reads its first source, Omerlo, through Omerlo's private backoffice API. This page explains how the team gets access, how the API works, and what that means for the connector.
- Official documentation: developers.omerlo.com/docs/backoffice
- Related: Architecture Decision Records (D6, D7, D8), Security, Tenancy
- Tickets: #23 (staging access), #53 (authentication), #54 (fetchers), #55 (incremental sync)
Change from the 2026-10-14 sync. The ADRs and the first connector work assumed Omerlo's Reader API with OAuth 2 client credentials and an
x-omerlo-media-idheader. Omerlo has since given us access to the backoffice API, authenticated with an API key that we create ourselves (D8). Whether we also get Reader API access is an open question below.
| Step | Who | Status |
|---|---|---|
| 1. Create an account on staging: staging.omerlo.app | Each team member | |
| 2. Post the email you signed up with in the Omerlo Discord thread | Each team member | |
| 3. Invite us to the staging tenant | Alex or nitnekb (Omerlo) | |
| 4. Give us admin access to the CMS | Omerlo | |
| 5. In the CMS, create an account of type integration | One team member (admin) | |
| 6. Generate an API key from the integration account | Same person | |
| 7. Store the key in the team's private credentials repository and share access there only | Same person |
Nobody sends us credentials. The API key is created by the team once we are admins on staging.
Rules for the key (Security):
- It goes in your local
.envand the private credentials repository only. It never goes in this repo, Discord, screenshots, CI logs or application logs. - One key per environment. Production keys are separate from staging and live in Fly.io secrets.
- If a key leaks, revoke it in the CMS and generate a new one before doing anything else.
| Environment | Base URL |
|---|---|
| Canada (production) | https://ca.omerlo.app/api |
| Europe (production) | https://eu.omerlo.app/api |
| Staging | To confirm with Omerlo; probably https://staging.omerlo.app/api
|
Every request sends the API key in the x-api-key header:
GET /community/v2/workspaces/{workspace_id}/organizations HTTP/1.1
Host: ca.omerlo.app
x-api-key: <API key from .env>The documentation also defines an api_key query parameter. We do not use it, because URLs end up in logs and browser history.
Community data is scoped by workspace: list endpoints take a workspace_id in the path. GET /composer/v2/workspaces lists the workspaces the key can see; each has an id, name, canonical_domain and locales.
How Omerlo workspaces map to PULSE tenants is to confirm (Tenancy). The likely mapping is one Omerlo client to one PULSE tenant, with one or more workspaces.
PULSE only reads. The API also creates, updates and deletes records, and the connector never calls those endpoints.
| Resource | List | Detail | Extra |
|---|---|---|---|
| Workspaces | GET /composer/v2/workspaces |
GET /composer/v2/workspaces/{id} |
|
| Organizations | GET /community/v2/workspaces/{workspace_id}/organizations |
GET /community/v2/organizations/{id} |
GET /community/v2/organizations/{organization_id}/metadata |
| People | GET /community/v2/workspaces/{workspace_id}/people |
Person by id | Person metadata |
| Events | List events (by workspace) | Event by id, with sessions | Event metadata |
| Projects | List projects (by workspace) | Project by id | Project metadata |
| Categories | Paginated categories | Category by id |
Paths not written out above follow the same pattern; see the documentation for each one.
List endpoints return a summary only. An organization in a list has its id, name, profile (profile_type_id, status_id, canonical_url), profile_image_key, inserted_at and updated_at.
The detail endpoint returns everything else, including the fields entity resolution needs:
| Field | Shape | Used for |
|---|---|---|
contact[] |
One entry per locale: email, phone, website, linkedin, facebook, instagram, twitter, bluesky
|
Identifier matching (#26, #61) |
address[] |
One entry per locale: street, city, country, location
|
Matching, display |
description[] |
Per locale, a list of blocks (text, image, slideshow, video) |
Display, relationships |
profile.profile_categories[] |
Category ids | Classification |
profile.workspace_id |
The workspace the record belongs to | Tenant scoping |
locales[] |
Per-locale summary | Display |
Metadata (key and value pairs, such as an external CRM id) comes from a separate /metadata endpoint.
So syncing one organization takes up to three calls: the list (shared across records), its detail, and its metadata.
Responses have data and a meta object with base, next and previous. next is null on the last page. The workspaces endpoint takes after, before and limit query parameters, so next is probably a cursor to send as after.
Neither the parameters on the community list endpoints nor the exact meaning of next is documented. Both are to confirm on staging.
List endpoints can be sorted by updated_at (sort_by=updated_at-desc), but there is no "updated since" filter.
An incremental sync therefore reads the list newest first and stops at the first record older than the last sync's watermark (D6, #55). Deleted records never appear in a list, so only the periodic full sync can detect them.
Every error body is { "error": "<message>" }.
| Status | Meaning | Connector behaviour |
|---|---|---|
| 400 | Invalid UUID | Bug on our side: log and fail the run |
| 401 | API key missing or invalid | Stop the run; the key must be checked. Never retry in a loop |
| 403 | Key lacks the role for this resource | Stop the run; ask Omerlo for the permission |
| 404 | Record not found (for example deleted between list and detail) | Skip the record and log it |
Rate limits for the backoffice API are not documented. The "no rate limits" answer from 2026-10-14 was about the Reader API, so ask again.
- Is the backoffice API the one PULSE should use, or will we also get Reader API access (OAuth client credentials)?
- What is the staging API base URL?
- How do pagination parameters work on the community list endpoints (
after,limit, maximum page size)? - Is there an "updated since" filter, or a planned webhook (the
Webhookssection of the docs covers event participants only)? - Are there rate limits?
- Which workspaces belong to which client, and should PULSE treat each client as one tenant?
- Can the integration account be read-only? PULSE never writes to Omerlo, so the key should not be able to.
PULSE Wiki
- Home
- Team Structure
- Product Roadmap
- Meeting Minutes
- Presentations
- Risks
- User Consent and EULA
- Legal and Ethical Issues
- PULSE Identity and Business Orientation
- Personas
- Diversity Statement
- Overall Architecture and Class Diagrams
- Architecture Decision Records
- Infrastructure and Tools
- Beyond-CRUD Flows
- Naming Conventions
- Code Style
- Testing Plan and Continuous Integration
- Security
- Performance
- Deployment Plan and Infrastructure
- Missing Knowledge and Independent Learning
-
Iteration and Release Notes
- Iteration 1 (Oct 6, 2026)
- Iteration 2 (Oct 20, 2026)
- Iteration 3 (Nov 3, 2026)
- Iteration 4 (Nov 17, 2026)
- Iteration 5 (Dec 1, 2026)
- Iteration 6 (Dec 15, 2026)
- Iteration 7 (Jan 19, 2027)
- Iteration 8 (Feb 5, 2027)
- Iteration 9 (Feb 16, 2027)
- Iteration 10 (Mar 2, 2027)
- Iteration 11 (Mar 16, 2027)
- Iteration 12 (Mar 30, 2027)
- Iteration 13 (Apr 13, 2027)