Skip to content
Browse the docs

API

Share links

Create a campaign link for one creator, or prepare links for many creators at once.

Endpoints

GET/campaigns/{campaignId}/creatorsEvery link on the campaign. Filter: active.
GET/campaigns/{campaignId}/creators/{externalId}One creator’s link.
POST/campaigns/{campaignId}/creators/{externalId}Create the link now. 201 when new, 200 if it exists.
PATCH/campaigns/{campaignId}/creators/{externalId}Pause or resume it.
POST/campaigns/{campaignId}/assignmentsQueue links for many creators. Returns 202.
GET/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.

Request
POST /campaigns/{campaignId}/creators/hb-1042
Response
{
  "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
}
FieldWhat it is
shareUrlThe link the creator shares. Open it in the system browser, not an embedded web view.
shortCodeEight letters and digits, shown in two groups separated by a dash. The Find page accepts the code with or without a dash or space.
statsUrlThe creator’s private figures page. Only they should see it.
activeWhether this campaign link is enabled. New claims also need an active campaign, open offer and campaign dates, and ready codes.
activeOnRosterBoolean roster status. Deactivating the roster entry does not pause existing links.
creator / campaignIdCreator reference { externalId, name } and the Viffy campaign ID.
assignedAt / deactivatedAtUTC timestamp when the link was created and nullable timestamp when paused. Resuming clears deactivatedAt.
Refused whenAnswer
The creator is not on the roster404 not_found. Add them first.
The campaign has ended409 conflict
The creator is deactivated409 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

Request
PATCH /campaigns/{campaignId}/creators/hb-1042
{ "active": false }
No new codes are handed out. Codes already handed out still work and still count for their creator.

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 createdResponse
No new links can be assigned, including when all selected creators already have links400 validation_failed
Another assignment is queued or running for this campaign409 conflict
The campaign has ended409 conflict
Request
POST /campaigns/{campaignId}/assignments
{ "externalIds": ["hb-1042", "hb-1043", "hb-9999"] }

// or every active creator on the roster
{ "scope": "roster" }
202 response
{
  "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.completed also signals completion, including individual failures, but is not sent for a failed job.
  • status is queued, running, completed or failed.
  • When it finishes, unassignableExternalIds lists every creator who could not get a link, in full.
FieldWhat it is
job.id / campaignId / scope / statusJob ID, campaign ID, selection mode (roster or selected), and current progress status.
job.totalCount / assignedCount / failedCountIntegers: creators selected for this job, links assigned, and creators it could not assign.
job.lastErrorExplanation of a problem, or null.
job.createdAt / startedAt / completedAtUTC timestamps; startedAt and completedAt are null until those steps happen.
selection.identifiersReadNumber of submitted IDs; null for a whole-roster selection.
selection.matched / alreadyAssigned / toAssign / unmatchedSelection counts before processing. Already linked creators are skipped, not reactivated.
selection.unmatchedSampleUp to 50 entries of { externalId, reason } for unmatched or inactive creators. It is not the complete failure list.
job.unassignableExternalIdsString 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.