API
Share links
Create a campaign link for one creator, or prepare links for many creators at once.
Endpoints
/campaigns/{campaignId}/creatorsEvery link on the campaign. Filter: active./campaigns/{campaignId}/creators/{externalId}One creator’s link./campaigns/{campaignId}/creators/{externalId}Create the link now. 201 when new, 200 if it exists./campaigns/{campaignId}/creators/{externalId}Pause or resume it./campaigns/{campaignId}/assignmentsQueue links for many creators. Returns 202./campaigns/{campaignId}/assignments/{jobId}The job’s progress.One link, now
The call your server makes when a creator joins a campaign. No body. It requires an active creator and a campaign that has not ended; creating a link does not activate the campaign or verify a shopper.
POST /campaigns/{campaignId}/creators/hb-1042{
"creator": { "externalId": "hb-1042", "name": "Jane Doe" },
"campaignId": "7c1e29a4-5b68-4d02-91f3-8a4c6e0b752d",
"shareUrl": "https://YOUR-VIFFY-ADDRESS/c/2lK9vNzQ8mT5xR7sB4cD6A",
"shortCode": "ABCD-2345",
"statsUrl": "https://YOUR-VIFFY-ADDRESS/s/Y3f9aQ6mR8sT2vW5xZ7bCA",
"active": true,
"activeOnRoster": true,
"assignedAt": "2026-08-28T15:04:05Z",
"deactivatedAt": null
}| Field | What it is |
|---|---|
shareUrl | The link the creator shares. Open it in the system browser, not an embedded web view. |
shortCode | Eight letters and digits, shown in two groups separated by a dash. The Find page accepts the code with or without a dash or space. |
statsUrl | The creator’s private figures page. Only they should see it. |
active | Whether this campaign link is enabled. New claims also need an active campaign, open offer and campaign dates, and ready codes. |
activeOnRoster | Boolean roster status. Deactivating the roster entry does not pause existing links. |
creator / campaignId | Creator reference { externalId, name } and the Viffy campaign ID. |
assignedAt / deactivatedAt | UTC timestamp when the link was created and nullable timestamp when paused. Resuming clears deactivatedAt. |
| Refused when | Answer |
|---|---|
| The creator is not on the roster | 404 not_found. Add them first. |
| The campaign has ended | 409 conflict |
| The creator is deactivated | 409 conflict |
Existing links and listing
Repeating the create call returns 200 with the existing link while the campaign has not ended and the creator is active. It keeps the link’s current status; use PATCH to resume a paused link.
List links with page and pageSize. The active filter checks the campaign link’s status, separately from the creator’s roster status.
Pause a link
PATCH /campaigns/{campaignId}/creators/hb-1042
{ "active": false }Pausing a link also makes its stats page unavailable. Reactivate the link to restore access, provided the creator is still active.
Create many links
Send a nonempty list of up to 10,000 creator IDs, or { "scope": "roster" } without externalIds. For an explicit list, omit scope or use selected. Creators who already have a link are skipped and counted as such.
| When no job is created | Response |
|---|---|
| No new links can be assigned, including when all selected creators already have links | 400 validation_failed |
| Another assignment is queued or running for this campaign | 409 conflict |
| The campaign has ended | 409 conflict |
POST /campaigns/{campaignId}/assignments
{ "externalIds": ["hb-1042", "hb-1043", "hb-9999"] }
// or every active creator on the roster
{ "scope": "roster" }{
"job": {
"id": "b41d827c-e09a-4a7e-b068-a957c36e120f",
"campaignId": "7c1e29a4-5b68-4d02-91f3-8a4c6e0b752d",
"status": "queued",
"scope": "selected",
"totalCount": 2, "assignedCount": 0, "failedCount": 0,
"lastError": null,
"unassignableExternalIds": [],
"createdAt": "2026-08-28T15:04:05Z", "startedAt": null, "completedAt": null
},
"selection": {
"scope": "selected",
"identifiersRead": 3,
"matched": 2, "alreadyAssigned": 0, "toAssign": 2, "unmatched": 1,
"unmatchedSample": [{ "externalId": "hb-9999", "reason": "No active creator has this Creator ID. Import the creator or reactivate them." }]
}
}- Poll
GET /campaigns/{campaignId}/assignments/{jobId}until completed or failed. The response is the job object alone;assignment.completedalso signals completion, including individual failures, but is not sent for a failed job. statusisqueued,running,completedorfailed.- When it finishes,
unassignableExternalIdslists every creator who could not get a link, in full.
| Field | What it is |
|---|---|
job.id / campaignId / scope / status | Job ID, campaign ID, selection mode (roster or selected), and current progress status. |
job.totalCount / assignedCount / failedCount | Integers: creators selected for this job, links assigned, and creators it could not assign. |
job.lastError | Explanation of a problem, or null. |
job.createdAt / startedAt / completedAt | UTC timestamps; startedAt and completedAt are null until those steps happen. |
selection.identifiersRead | Number of submitted IDs; null for a whole-roster selection. |
selection.matched / alreadyAssigned / toAssign / unmatched | Selection counts before processing. Already linked creators are skipped, not reactivated. |
selection.unmatchedSample | Up to 50 entries of { externalId, reason } for unmatched or inactive creators. It is not the complete failure list. |
job.unassignableExternalIds | String array of selected creators the finished job could not assign. Fix or reactivate those creators before submitting them again. |
Completed does not mean every requested creator received a link. Check selection.unmatched and job.failedCount before distributing links.