Inviting people with campaigns
A campaign is how people get invited to assessments on Deeper Signals. It defines which assessments and reports participants receive and who is invited, and it owns the invitation e-mail and the sign-in link. Campaigns come in two types:
campaign_type | For |
|---|---|
DEVELOPMENT | Employees: the person is created (or reused) as a regular user of the account. |
HIRING | Candidates: the person is a candidate until hired. |
Use a campaign whenever you want someone to take an assessment. Creating a user alone (POST /user) does not assign anything.
The campaign endpoints mirror what administrators do in the admin app: the same create form, the same campaign shape and the same outcomes when people are added, invited or removed.
The flow
1. Pick assessments and reports
GET /assessment_libraries lists the assessments enabled on your account; GET /reports lists the reports and which assessments each needs (required_assessment_library_ids).
2. Create the campaign
curl -X POST https://api.deepersignals.com/api/external/campaigns \
-H "X-TOKEN: $DS_API_TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Q4 onboarding",
"campaign_type": "DEVELOPMENT",
"assessment_libraries": [24],
"user_report_libraries": [6],
"groups": [135]
}'
| Field | Required | Description |
|---|---|---|
name | yes | Shown to administrators and in the invitation e-mail. |
assessment_libraries | yes | Ids from GET /assessment_libraries. |
user_report_libraries | no | Report ids from GET /reports participants may see once done. |
groups | no | Group ids participants join. |
campaign_type | no | DEVELOPMENT (default) or HIRING. |
notify_on_completion, notify_on_completion_users | no | E-mail the given admins when a participant completes. |
job_role, competencies, competency_content_libraries | no | Hiring campaigns: the role profile to match candidates against. |
ats_integrated_campaign, dc_integrated_campaign, dc_integration_provider, dc_integration_redirect_url | no | Integration flags; the data-collection settings require a platform administrator. |
The 201 response is the campaign in the same shape as the admin app shows it (id, name, enabled, campaign_type, assessment_libraries, user_report_libraries, usergroups, invite_token, ...) plus invite_link_template: the URL the invitation e-mails carry with a {token} placeholder. Replace {token} with the campaign's invite_token to get the campaign's public sign-up link. Anyone who opens it can sign up to the campaign, so share it only when that is what you want, for example on an intranet page.
invite_link_template: https://app.deepersignals.com/auth/verify/campaign/{token}?account=examplecorp
invite_token: 8f3c1d...
public sign-up link: https://app.deepersignals.com/auth/verify/campaign/8f3c1d...?account=examplecorp
GET /campaigns/{campaign_id} returns the same object plus progress counts (invited, signed up, completed).
3a. Let Deeper Signals send the invitation
POST /campaigns/{campaign_id}/invitations with emails and/or user_ids. Each person is added to the campaign (created as a user if needed) and receives the invitation e-mail. Calling it again for an existing participant re-sends the e-mail.
{ "emails": ["jane.doe@example.com", "john.roe@example.com"], "user_ids": [2041] }
{
"affected_count": 2,
"queued": false,
"skipped": [ { "identifier": "john.roe@example.com", "user_id": 2042, "reason": "completed" } ]
}
More than 10 recipients are sent in the background: the response is 202 with queued: true.
3b. Or deliver the link yourself
-
POST /campaigns/{campaign_id}/usersadds people without sending anything and tells you who was added (was_new_userwhen the e-mail had no user yet) and who was skipped.{"added": [ { "user_id": 2041, "email": "jane.doe@example.com", "was_new_user": true } ],"skipped": [ { "identifier": "john.roe@example.com", "user_id": 2042, "reason": "already_recipient" } ]} -
POST /campaigns/{campaign_id}/users/tablereturns the participants as a table. Select theusers.invite_campaign_user.tokencolumn: it is the participant's personal invite token. The response also carriesinvite_link_template; replace its{token}with the participant's token and you have the same link the invitation e-mail would have carried. Put it in your own onboarding e-mail, HR portal or chat message. The participant'semail_statusstays untouched, so administrators can see the e-mail was not sent by the platform.{"items": [{ "users.id": 2041, "users.email": "jane.doe@example.com", "users.invite_campaign_user.token": "c71e9a..." }],"total": 1,"pages": 1,"invite_link_template": "https://app.deepersignals.com/auth/verify/campaign/{token}?account=examplecorp"}
A personal link signs that person straight into their assessment. Treat it as a credential: send it only to its owner.
Skip reasons
reason | Meaning |
|---|---|
already_recipient | Already on this campaign (add). |
unknown_user_id | No such user on your account. |
unavailable | The user cannot be invited (for example deactivated or an API user). |
anonymous | Anonymous users cannot be invited. |
not_a_recipient | Not on this campaign (remove). |
completed | Already completed the campaign's assessments (invite). |
already_sent | An invitation was already sent and cannot be repeated yet. |
locked_hired | Hired candidate on a hiring campaign; cannot be changed. |
not_removable | The participant has started and cannot be removed. |
failed | Unexpected error for this person; the others were processed. |
At most 100 people per call.
4. Track progress
POST /campaigns/{campaign_id}/users/tablewith nocolumnsreturnsusers.id,users.email, names,users.invite_campaign_user.status(UNUSED,SIGNED_UP,IN_PROGRESS,COMPLETED),email_status,created_at,completed_assessmentsand, for hiring campaigns,hiring_statusandmatch_score. Filter like any table query, for example{ "field": "users.invite_campaign_user.status", "type": "eq", "value": "COMPLETED" };totalandpagescome back with every page.GET /campaigns/{campaign_id}carriesprogress.- The
AssignedAssessmentFinishedwebhook fires per completed assessment, which is the cheapest way to know when results are ready.
5. Remove someone
DELETE /campaigns/{campaign_id}/users/{user_id} takes a participant who has not started off the campaign. The response is affected_count: 1, or 0 with a skipped reason (not_a_recipient, not_removable).
After completion
Once a participant has completed, read their scores with GET /users/{user_id}/results, check or change which reports they see with the participant report endpoints, and download PDFs or create share links. The person keeps their account; invite them to the next campaign the same way.
Listing campaigns
POST /campaigns/table is a table query over the account's campaigns; GET /campaigns/columns lists its columns. With no columns it returns invite_campaign.id, name, campaign_type, enabled, created_at, the invited groups, the assessment names and the completed count.