Skip to content

Outbound Webhooks

Receive real-time notifications when data changes in Flowstate. Configure webhook endpoints to push events to your systems whenever employees, teams, projects, allocations, and other entities are created, updated, or deleted.

Overview

Flowstate webhooks deliver events from all entry points:

  • GraphQL mutations — direct changes by users in the UI
  • REST API — changes made via API keys
  • Scenario merges — when a plan/scenario is merged to live data (including changes made via MCP tools and AI chat)

Events are delivered asynchronously. Mutations are never blocked by slow or failing endpoints.

Configuring endpoints

There are no REST endpoints for managing webhook configuration. You configure outbound webhooks in the app at Settings → Webhooks (/settings/webhooks).

Each webhook endpoint has:

OptionDescription
NameDisplay name (max 100 characters), e.g. "HRIS Sync Endpoint"
Endpoint URLThe HTTPS URL that receives event deliveries
Entity typesThe entity types to subscribe to (see table below). Events are only delivered for subscribed types.
Event typesAny combination of create, update, delete
EnabledA per-endpoint toggle. Disabled endpoints receive no deliveries but keep their configuration.

When you create an endpoint, Flowstate generates a signing secret and shows it once. Store it securely; it is not shown again. From the endpoint's actions menu you can also:

  • Test Connection: sends a test delivery so you can verify reachability and your signature verification.
  • Rotate Secret: generates a new signing secret and shows it once. Update your handler immediately after rotating; the old secret stops being valid.

The settings page shows each endpoint's last delivery time and result, so you can spot failing endpoints at a glance.


Entity Types

Subscribe to the specific entity types you need. Events are only delivered for entity types your endpoint is subscribed to.

Core Entities

Entity TypeDescription
employeeFull-time and part-time staff members
teamOrganizational teams and hierarchy
projectProjects with timelines and cost tracking
contractorExternal contractors and consulting firms
vacancyOpen positions and hiring pipeline
userPlatform user accounts
initiativeThe finance/governance anchor (cost centre, Finance Mode, budget, rollups)

Allocations

Entity TypeDescription
employee_team_allocationEmployee assignments to teams (with FTE)
employee_project_allocationEmployee assignments to projects (with FTE)
team_project_allocationTeam assignments to projects (with FTE)
contractor_team_allocationContractor assignments to teams
contractor_project_allocationContractor assignments to projects
vacancy_team_allocationVacancy assignments to teams
vacancy_project_allocationVacancy assignments to projects

Compensation

Entity TypeDescription
salary_adjustmentEmployee salary changes (effective-dated)
bonusEmployee bonus records
contractor_rateContractor rate adjustments

Reference Data

Entity TypeDescription
cost_centerBudget tracking and financial allocation
value_streamBusiness capability groupings for projects
driverProject business driver types
objectiveStrategy layer above Initiatives (with Key Results)
portfolio_lensFinance-sliceable Initiative classification (KTLO / Revenue / …)
capex_claimCapEx/R&D claim headers (Initiative-anchored or ad-hoc)
exchange_rateCurrency conversion rates
geographyGeographic locations (labelled Locations in the app)

AI

Entity TypeDescription
ai_agentResourced AI agents (planned AI workers)
agent_policyAgent policy configuration changes
ai_policyAI usage policy changes

Event Types

EventDescription
createA new entity was created. before is null.
updateAn existing entity was modified. Both before and after are present.
deleteAn entity was deleted. after is null.

Payload Format

Every webhook delivery is an HTTP POST with a JSON body:

json
{
  "id": "d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8g",
  "timestamp": "2026-03-18T15:30:00.000Z",
  "entity_type": "employee",
  "entity_id": "clx1a2b3c4d5e6f7g8h9",
  "change_type": "create",
  "initiator": {
    "type": "user",
    "id": "clx9u8s7e6r5",
    "email": "jane.chen@acme.com"
  },
  "organization_id": "clx9o8r7g6i5d4",
  "before": null,
  "after": {
    "id": "clx1a2b3c4d5e6f7g8h9",
    "firstName": "Alex",
    "lastName": "Rivera",
    "email": "alex.rivera@acme.com",
    "startDate": "2026-04-01T00:00:00.000Z",
    "jobRoleId": "clx9r8q7w6e5",
    "geographyId": "clx3g2h1j0k9",
    "defaultCurrencyCode": "GBP"
  }
}

Payload Fields

FieldTypeDescription
idstring (UUID)Unique event identifier (for idempotency)
timestampstringISO 8601 timestamp of when the event occurred
entity_typestringEntity type (see tables above)
entity_idstring (CUID)ID of the affected entity
change_typestringcreate, update, or delete
initiator.typestringWho triggered the change (see below)
initiator.idstringID of the actor (user ID or API key ID)
initiator.emailstring or nullEmail of the actor (present for user type)
organization_idstring (CUID)Organization the event belongs to
beforeobject or nullEntity state before the change (null for creates)
afterobject or nullEntity state after the change (null for deletes)

Initiator Types

TypeDescription
userChange made by an authenticated user via the UI or GraphQL
api_keyChange made via the REST API with an API key
mergeChange applied by merging a scenario (covers MCP tools, AI chat)
systemSystem-triggered change (reserved for future use)

Signature Verification

Every delivery includes an HMAC-SHA256 signature in the X-Flowstate-Signature header. Verify this signature to ensure the payload was sent by Flowstate and has not been tampered with.

Headers

HeaderDescription
X-Flowstate-Signaturesha256=<hex-encoded HMAC>
X-Flowstate-Event-IdUnique event UUID (same as id in body)
Content-Typeapplication/json

Verification Example (Node.js)

javascript
import crypto from 'crypto';

function verifyWebhookSignature(body, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post('/flowstate/webhook', (req, res) => {
  const signature = req.headers['x-flowstate-signature'];
  const rawBody = req.body; // Must be the raw string, not parsed JSON

  if (!verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(rawBody);
  console.log(`Received ${event.change_type} on ${event.entity_type}: ${event.entity_id}`);

  res.status(200).send('OK');
});

Verification Example (Python)

python
import hmac
import hashlib

def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Secret Rotation

Rotate the signing secret from the endpoint's actions menu at Settings → Webhooks. The new secret is shown once. Update your handler immediately after rotating; the old secret stops being valid as soon as it is rotated.


Test Delivery

Use Test Connection on the endpoint at Settings → Webhooks to verify your endpoint is reachable and your signature verification works. Flowstate delivers a test event with entity_type: "employee", change_type: "create", and sample data.


Delivery Behaviour

PropertyValue
Timeout5 seconds per delivery attempt
MethodPOST with Content-Type: application/json
DeduplicationUse X-Flowstate-Event-Id or id in the payload for idempotency
OrderingEvents are delivered in approximate order but not guaranteed
Plan modeChanges within a scenario do NOT fire webhooks
MergeWhen a scenario is merged, one event fires per entity changed

INFO

Your endpoint should return a 2xx status code to acknowledge receipt. Non-2xx responses are logged as failures. The delivery timeout is 5 seconds — if your processing takes longer, acknowledge the event immediately and process it asynchronously.


Best Practices

  1. Always verify signatures. Reject requests with missing or invalid X-Flowstate-Signature headers to prevent spoofing.

  2. Use the event ID for idempotency. In rare cases, the same event may be delivered more than once. Use the id field to deduplicate on your end.

  3. Respond quickly. Return 200 OK as soon as you receive the event. Process the payload asynchronously if needed.

  4. Subscribe only to what you need. Each endpoint can filter by entity type and event type. Subscribing to fewer types reduces noise and delivery volume.

  5. Monitor delivery status. Check each endpoint's last delivery result at Settings → Webhooks periodically. Persistent failures may indicate endpoint issues or network problems.

  6. Rotate secrets regularly. Treat the signing secret like a credential. Rotate it on a schedule that matches your security policy.

Flowstate Documentation