Skip to content
NoClutterCRM

Send events. Choose what happens next.

Send an event name and payload from your server. NoClutterCRM saves the event and evaluates the active workflows you configure for it.

Event sources require automation access on your workspace. Reading this guide does not require an account.

1. Create an API key

  1. Sign in, select the brand that should own the events, and open Automations → Event sources.
  2. Under Create event source, enter a Source name, such as Website or Zenkoo, then choose Create API key.
  3. Copy the key into your server’s secret configuration. It is shown only once. Choose Test connection to verify it without creating an event or sending a message.

The source name is a label, not a connection to your application. The key identifies its tenant, brand, and source. One source is enough to start; use separate keys for different brands or when you want to distinguish senders.

Your platform administrator enables this through the tenant’s Automations preview feature. If access is later removed, source settings are blocked and pending workflow evaluations are suppressed. Existing keys can still save events without running workflows; disabling a source rejects new events entirely.

You need permission to manage automations and the selected brand’s settings. Keep the key on your server, never in frontend JavaScript or the public site tag. Use Replace key to revoke an old key, or Disable to stop receiving events from that source.

2. Send your first event

After the action succeeds in your application—for example, after committing a new account—send a POST request. Set NOCLUTTER_API_KEY in your server environment first. This example creates a real event in the brand attached to your key.

curl --request POST 'https://nocluttercrm.com/api/events' \
  --header "Authorization: Bearer $NOCLUTTER_API_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header 'Idempotency-Key: signup-123-created' \
  --data '{
    "event": "account.created",
    "payload": {
      "customer_id": "123",
      "email": "person@example.com",
      "plan": "pro"
    }
  }'

Only event and payload belong at the top level. The event receiver accepts an empty payload. Contact workflows have additional identity requirements described below. Custom names such as project.created work in the same way as account.created.

A successful response has HTTP status 202:

{
  "accepted": true,
  "event_id": "evt_noclutter_assigned_id",
  "duplicate": false
}

NoClutterCRM assigns the event ID. Accepted means the event was saved; it does not mean a workflow or message has completed.

Identify the same user across events

For Contact workflows, send your application’s stable person ID as payload.customer_id on every relevant event. Use the same nonblank string after email or name changes. Do not generate a new customer ID per event, use an email address as the ID, or reuse a deleted person’s ID for someone else. IDs are case-sensitive and limited to 190 characters.

Your API key identifies the event source, tenant and brand. The saved link is event source + customer ID → CRM Contact, within that tenant and brand. Use separate sources for independent applications and test/production environments. If user IDs are unique only within your app’s own workspaces, send a globally unique ID or a stable, collision-free composite. Replacing a key preserves the source’s links; deleting and recreating the source does not.

  1. Signup: send customer_id, email, and optionally name. Configure Match or create a Contact on your signup event. It uses the saved app ID first, then an unambiguous email match within this brand. New or profile-registered Unknown Contacts become Leads; existing Customers keep their lifecycle.
  2. Later events: keep the same customer_id. Configure Update linked Contact to record a workspace or WhatsApp connection status. It requires a saved link or a valid email to establish one, plus workspace_id; connection updates also require whatsapp_account_id.
  3. Review first: save the draft and run a dry run. Missing links, identity conflicts and unavailable sources block the action. Receiving events or saving drafts does not create Contacts or activate workflows.
{
  "event": "account.created",
  "payload": {
    "customer_id": "usr_42",
    "email": "person@example.com",
    "name": "Asha Rao"
  }
}

{
  "event": "workspace.created",
  "payload": {
    "customer_id": "usr_42",
    "workspace_id": "org_8"
  }
}

These are two separate requests, with different Idempotency-Key values. The customer ID identifies the person; the retry key identifies one event. Reuse a retry key only when retrying that same event with unchanged data.

Choose whose record the event concerns. A workspace owner, the teammate performing an action, and a WhatsApp customer can be different people. A workspace or connection ID is not a person ID. Anonymous and system events may omit identity, but cannot perform Contact actions that require it.

Registration/import preserves CRM names, email addresses, lifecycle and consent. An explicit signup action can mark a new or profile-registered Unknown Contact as Lead. Email matching does not verify ownership or grant marketing consent. If your application uses shared or unverified addresses that cannot reliably identify a person, do not activate automatic email matching. Conflicting IDs/emails and Contacts belonging only to another brand stop for review; they are never automatically merged.

Register new and existing users

Use the same API key with POST /api/events/users/upsert for one profile, or POST /api/events/users/import with a users array of up to 100 profiles. Import existing accounts without sending historical signup events. Registration does not create automation events, mark users as Leads, or send messages.

{
  "customer_id": "usr_42",
  "revision": 1,
  "email": "asha@example.com",
  "name": "Asha Rao",
  "attributes": {"plan": "pro"}
}

Send a full profile snapshot with a stable customer_id, a positive integer revision, an email and a name (null when unknown). Increase the revision whenever the profile changes; retry the same snapshot with the same revision. Optional attributes contain up to 30 flat scalar fields. Optional account_created_at uses an ISO timestamp with timezone and whole seconds. Omitted optional fields reset only the app snapshot. Requests are limited to 32 KiB per user and 256 KiB per batch.

A 200 response reports created, updated, unchanged or stale. Reusing a revision with different data returns 409. Imports return an indexed result for every row, including invalid or conflict failures; inspect all results and checkpoint progress in your app. NoClutterCRM preserves CRM edits and consent, and displays app facts and the connected app user ID separately on the Contact.

For new signups, register the profile first, then send the genuine account.created event. Later events reuse the same customer_id. A matching active workflow that needs an unknown user can wait up to seven days when only the user ID was supplied. Registering that user lets the original event resume on the scheduler; it does not create a new signup. An expired dependency needs review. Importing may therefore release already received real events; keep workflows inactive during review.

Current limits: profile snapshots are versioned, but workspace and connection state uses event receipt order. CRM primary email changes, arbitrary field mappings, app account aliases, automatic deletion and two-way sync are not provided. The 0.1.0-rc.2 SDK downloads include upsertUser/importUsers helpers for registration and imports.

Download the event SDKs

Use the NoClutterCRM server-side SDKs to validate payloads, retry temporary failures with the same delivery key, and test your connection. Version 0.1.0-rc.2 is a release candidate, distributed here as downloadable archives. These packages are not yet published to npm or Packagist.

Node.js

Node.js 22 and 24. TypeScript types, ESM and CommonJS. No framework required.

Includes the built client, types, README and license. Server-side use only.

Laravel

Laravel 12 on PHP 8.2+, or Laravel 13 on PHP 8.3+. Includes an injectable client, facade and test fake.

Download Laravel ZIP

Includes source, publishable configuration, README and license. Install with Composer below.

Download SHA-256 checksums to verify the archives. Keep API keys in server configuration. The SDKs deliver events directly; they do not install model observers or a background outbox.

Install and test your connection

Node.js

Download the npm package above, then run this from your server application, using the path where you saved the file:

npm install ./nocluttercrm-events-0.1.0-rc.2.tgz
import { NoClutterCRM } from '@nocluttercrm/events';

const client = new NoClutterCRM({
  apiKey: process.env.NOCLUTTER_API_KEY,
});

await client.testConnection(); // Does not create an event or send a message.

const receipt = await client.track('account.created', {
  customer_id: '123',
  email: 'person@example.com',
  plan: 'pro',
});

Laravel

Extract the Laravel ZIP into packages/nocluttercrm-laravel inside your application. That directory should contain composer.json. Add it as a local Composer package:

composer config repositories.noclutter path ./packages/nocluttercrm-laravel
composer require nocluttercrm/laravel:@dev
php artisan vendor:publish --tag=noclutter-config

Set NOCLUTTER_API_KEY in your server environment and rebuild your configuration cache. Test the configured key without creating an event:

php artisan config:cache
php artisan noclutter:test-connection
use NoClutterCRM\Laravel\Facades\NoClutter;

$receipt = NoClutter::track('account.created', [
    'customer_id' => (string) $user->id,
    'email' => $user->email,
    'plan' => 'pro',
]);

Choose when your application calls track(). The SDK generates a retry key once per call and reuses it for internal retries. For retries across jobs or processes, save the payload and a stable retry key together, then pass { idempotencyKey: savedKey } as the third Node argument or $savedKey as the third Laravel argument.

For crash-safe background delivery, save an outbox entry in the same database transaction as the business change. Queueing after commit alone leaves a crash window. A locally queued event has no server event ID until accepted. The included READMEs document timeouts, retry bounds, error handling and testing.

3. Make retries safe

Generate one unique Idempotency-Key for each logical event and save it with the request. Reuse that key and the same data for every retry. The example key above is for one signup only; choose a different key for the next event.

  • Same source, key, and data: the original event ID is returned with duplicate: true, without another workflow evaluation.
  • Same key with different data: HTTP 409. Fix the mismatch; do not blindly retry.
  • No retry key: every request is a new event, even if the bodies are identical.
  • Key rotation preserves retry protection because retries belong to the source, not its current secret.

Retry network failures, HTTP 429, and 5xx responses with backoff and the same key. Respect Retry-After when returned. A timeout can happen after the event was saved.

4. Choose a workflow

  1. Create a workflow and choose An event is received.
  2. Select your source and enter the exact event name, such as account.created.
  3. Optionally set Payload field to plan and Payload equals to pro.
  4. Add Notify team, review the dry run, and activate the workflow.
  5. Send a new matching event, then check Event history and the workflow activity.

Dry runs preview historical matches without executing actions. Only workflows active when a new event is accepted are eligible to run. Activating a workflow does not replay earlier events.

What this release does not do

An email address in the payload does not automatically resolve or create a CRM contact. Without explicit recipient selection, customer-email actions and sequence enrollment are blocked for this trigger. Choose the event email action to use its email and customer_id, with optional name. An activated email action may match or create a Contact in this brand; use the dedicated Match or create a Contact action to establish a persistent app customer-ID link; contacts from other brands require an explicit link first. Use {{first_name}} for a name with a “there” fallback. Reminders require send_now=true and a future expires_at, checked again before delivery. Sequence enrollment and general Contact actions remain unavailable without Contact context. The dedicated identity and linked-update actions above resolve their own event identity. Internal team notifications can run.

Payload fields currently support trigger equality matching. Arbitrary payload values cannot yet be inserted into action templates or used in general workflow branch conditions. Event names do not create, update, or deactivate contacts by themselves.

Dispatch runs every minute and claims up to 100 events per pass; queue load can add delay. Processed means matching finished, not that every action succeeded. Inspect individual workflow outcomes. Disabled sources and unavailable tenants or brands suppress pending evaluation.

API reference

Event request requirements
FieldRequirement
AuthorizationBearer API key. Tenant, brand, and source come from this key.
eventRequired; up to 120 characters. At least two dot-separated segments, each starting with a lowercase letter and using lowercase letters, digits, or underscores.
payloadRequired JSON object; may be empty. Up to 30 fields. Nested objects and arrays are not supported.
Payload fieldsNames start with a lowercase letter and contain lowercase letters, digits, or underscores; maximum 64 characters. Values: strings up to 1,000 characters, finite numbers, booleans, or null.
Idempotency-KeyOptional header; 1–160 printable ASCII characters without spaces.
Request sizeMaximum 32 KiB.
Rate limit120 requests per minute per client IP, shared by ingestion and connection tests. Sources sharing an outbound IP share this allowance.

Strings compare exactly in the trigger condition. For numbers, booleans, and null, enter their JSON text: 1, true, or null. Missing fields never match. Avoid credentials and unnecessary personal data in payloads.

To check a key without storing an event, send POST https://nocluttercrm.com/api/events/test with the same Authorization header. Success returns HTTP 200 with connected: true.

Troubleshooting

401 — Invalid credentials
Check the full bearer key. A replaced key stops working immediately.
403 — Workspace or brand unavailable
The tenant may be suspended or deleted, or the brand deleted. Ask your workspace administrator.
409 — Source disabled or retry conflict
Read the response message. Enable the source when appropriate, or check whether the same retry key was used with different data.
413 or 422 — Request rejected
Check the size, event name, flat payload structure, and field limits. Remove extra top-level fields such as email or event_id.
429 — Too many requests
Back off and retry with the same delivery key. Other sources on the same outbound IP share the limit.
Accepted, but no workflow ran
Check the source, event name, payload condition, and workflow activation time. Recorded means automation access was absent at acceptance; processed may mean no workflow matched. Pending or queued events need the scheduler and worker to run.
Open Event sources