Directory Sync #
Your ECW team-member directory is the list of people your organization celebrates and manages — it powers automations (birthdays, work anniversaries, recognition) and directory-restricted sending. Team members don't log into ECW — keeping their records in sync is a data problem, not a login problem, and it's fully independent of SSO.
Choose your sync method #
| Method | Best for | Requires | Guide |
|---|---|---|---|
| SCIM from your IdP (Okta, Entra ID, OneLogin, …) | Orgs whose IdP already holds the employee list | Enterprise plan + verified domain (SSO not required — pick SCIM-only in the wizard) | SCIM provisioning |
| HR platform → IdP → SCIM (ADP, Workday, UKG, …) | Orgs whose source of truth is an HR system | Same as SCIM + HR-driven provisioning into your IdP (standard IT plumbing) | Below |
| Directory Sync API | Custom integrations, HR/CRM systems without an IdP in the middle, scheduled exports | An API key | Below |
| Zapier | No-code sync from 6,000+ apps (BambooHR, Google Sheets, …) | Zapier account | Zapier guide |
| Spreadsheet import | Initial backfill, one-time loads | Nothing extra | In-app: Directory → Import |
| Manual | Small teams | Nothing extra | In-app: Directory |
These compose: a common setup is a spreadsheet backfill first, then SCIM or API/Zapier for ongoing deltas.
ECW is receive-only. Whatever the method, your system is the source of truth — ECW never writes back to your IdP or HR platform. To be notified when the ECW directory changes, subscribe to member-change webhooks.
Syncing from HR platforms (ADP, Workday, UKG, …) #
The most common ask: "our people live in ADP/Workday/UKG — can we sync them into ECW?" Yes — but not by pointing ECW at the HR platform directly. HR systems are the source of employee identity, not a sync authority: none of them can push SCIM to a third-party app (that's an IdP capability, not an HR-platform capability — true for every SaaS vendor, not just ECW).
The supported pattern — your HR platform feeds your identity provider, and the IdP provisions ECW:
ADP / Workday / UKG → Entra ID or Okta → SCIM → ECW team directory
(HR-driven provisioning) (SCIM-only mode — no SSO needed)
The left leg is standard IT plumbing: Workday → Entra/Okta connectors are first-party, and ADP/UKG are covered by Entra's API-driven inbound provisioning and partner connectors. Once in place, new hires flow HR → IdP → ECW automatically, and offboarded employees are deactivated the same way.
No IdP in the middle? Use a scheduled export from your HR system into the Directory Sync API below (e.g. a Workday RaaS report or ADP export on a cron), Zapier, or a periodic spreadsheet import.
Platform notes:
- Rippling — unlike most HR platforms, Rippling is also an identity provider; for SSO see the IdP compatibility table. For directory sync, treat it like an IdP if your plan supports custom app provisioning, otherwise use the API/Zapier.
- ADP — ADP's own integrations (SSO or data) are available only to apps listed on ADP Marketplace (a vendor partnership, not a setting your admin can configure). ECW is not currently an ADP Marketplace app — contact us if this matters for your organization. Meanwhile, the ADP → IdP → SCIM chain above works today.
- Workday / UKG — no customer-configurable identity-provider or outbound-SCIM capability. Use the IdP your tenant feeds, or a scheduled report export into the API.
Directory Sync API #
Sync team-member records from any system — HR/HRIS (Workday, BambooHR), CRM (Salesforce), or your own tools. These are the same endpoints the official Zapier integration uses, so you can build directly or use Zapier.
Base path: /v2/api/pub/team-member-actions
Auth: Authorization: Bearer <YOUR_API_KEY> (see getting-started).
Response envelope: { "success": …, "data": …, "messages": [] } (list endpoints add meta).
Create or update a member — POST /upsert #
Adds a member, or updates them if they already exist.
- PATCH semantics: only the fields you send are changed; omitted fields are left untouched, so a partial sync never blanks existing data. To clear a field, send it as an empty string.
- Matching: by default members match by email. To survive email changes, send a stable
employee_idand setmatch_fieldtoemployee_id— then an email change updates the same person instead of creating a duplicate.
Body fields #
| Field | Notes |
|---|---|
first_name |
required |
email |
required, must be valid |
last_name, job_position, profile_image_url |
optional strings (profile_image_url must be publicly fetchable) |
employee_id |
stable external ID (recommended for HR syncs) |
manager_email |
resolved to an existing member → manager link; if not present yet, resolves on a later sync |
department or departments[] |
one or more names; auto-created if missing. Send one or the other — if both are present, departments[] is used and department is ignored. |
date_of_birth, hire_date, custom_date_1..N |
dates; accepts YYYY-MM-DD, M/D/YYYY, etc. (birthday year is privacy-masked automatically; Feb 29 → Feb 28) |
active |
false deactivates, true reactivates; omit to leave unchanged (an update never silently reactivates) |
match_field |
email (default) or employee_id |
mode |
add_update (default) or add_new_only (skip if the member already exists) |
skip_if_missing_required |
true → skip rows missing first_name/email instead of erroring (invalid emails still error) |
dry_run |
true → validate and report the would-be action without saving |
Example #
curl -X POST https://app.ecardwidget.com/v2/api/pub/team-member-actions/upsert \
-H "Authorization: Bearer $ECW_API_KEY" -H "Content-Type: application/json" \
-d '{
"first_name": "Grace", "last_name": "Hopper", "email": "[email protected]",
"employee_id": "EMP-100", "match_field": "employee_id",
"job_position": "Engineer", "manager_email": "[email protected]",
"departments": ["Engineering", "Leadership"], "hire_date": "2021-03-01"
}'
Response data #
{ "action": "created", "id": 123, "changed_fields": ["job_position"], "warnings": [] }
action is one of created, updated, unchanged, or skipped. Sending active:false still
reports created/updated (the member is just marked inactive) — the deactivated / reactivated /
deleted actions come from the dedicated deactivate/reactivate/delete endpoints.
Find a member — GET /find #
GET /[email protected]
GET /find?employee_id=EMP-100
Returns the member in data and meta.found. data is empty when not found.
List members — GET /list #
Paginated, for reconciliation / verifying a sync.
| Query param | Notes |
|---|---|
status |
active (default), deactivated, or all |
perPage |
page size, ≤ 500 |
after_id |
cursor — pass the previous page's meta.next_after_id |
updated_since |
ISO datetime — only members changed since then (delta pulls) |
meta includes next_after_id and has_more.
Removing people (the streaming-safe replacement for a spreadsheet "Sync" that deletes everyone not in the file): periodically
GET /list, diff against your source system, anddeactivateanyone no longer present.
Deactivate / Reactivate — POST /deactivate, POST /reactivate #
Body: { "email": "…" } or { "employee_id": "…" }.
Deactivation is reversible: the member stops receiving automated cards and no longer counts toward your seat limit, but their record/history is kept. Reactivate to restore (subject to seat availability).
Delete (permanent) — POST /delete #
Body: { "email": "…" } or { "employee_id": "…" }.
Irreversible — for GDPR erasure only. For normal offboarding use deactivate.
Field discovery — GET /fields, GET /departments #
GET /fields— lists mappable fields (standard fields + your active custom date fields) withlabel,type,required, and acceptedformat(e.g. date formats). Use this to drive field mapping in your integration.GET /departments— lists your departments as{ "id": …, "name": "…" }.
curl https://app.ecardwidget.com/v2/api/pub/team-member-actions/fields \
-H "Authorization: Bearer $ECW_API_KEY"
Initial backfill #
For the first load of an existing employee base, use the in-app spreadsheet importer (or page
through many upsert calls). Use the API/Zapier for ongoing deltas thereafter. The bulk importer does
not emit member-change webhooks — those fire only for per-record changes.
Notifications when the directory changes #
To push changes back to another system, subscribe to member-change webhooks.