Skip to content
Browse the docs

API

Creators

Add and update creators using the IDs from your own system.

Endpoints

GET/creatorsThe roster. Filters: active, updatedSince.
GET/creators/{externalId}One creator.
PUT/creators/{externalId}Add or update one. 201 when new, 200 otherwise.
POST/creators/batchAdd or update up to 1,000 in one call.

One creator

Request
PUT /creators/hb-1042
{ "name": "Jane Doe", "email": "[email protected]", "handle": "@janedoe", "active": true }
Response
{
  "externalId": "hb-1042",
  "name": "Jane Doe",
  "email": "[email protected]",
  "handle": "@janedoe",
  "active": true,
  "createdAt": "2026-08-28T15:04:05Z",
  "updatedAt": "2026-08-28T15:04:05Z"
}
  • The externalId in the path is yours. Repeating the call with the same id updates the creator without creating a duplicate.
  • email and handle: leave a field out to keep it, send null to clear it.
  • active defaults to true for a new creator. false prevents new campaign links and hides the creator’s stats page.
  • Existing campaign links keep handing out codes until paused separately. Pausing a link also makes its stats page unavailable.
FieldRule
externalIdRequired. One line, up to 100 characters. Cannot change.
nameRequired for a new creator; omit or null keeps the existing name on an update. Nonblank, one line, up to 200 characters.
emailOptional. Valid email address, up to 254 characters. null or empty clears it.
handleOptional. One line, up to 100 characters. null or empty clears it.

Response fields

FieldWhat it is
externalId / nameStrings: your immutable creator ID and the current display name. IDs are case-sensitive; surrounding whitespace is trimmed on writes. URL-encode an ID used in a path.
email / handleString or null when not set.
activeBoolean roster status, separate from each campaign link’s active status.
createdAt / updatedAtUTC timestamps for creation and the most recent change. Unchanged upserts leave updatedAt unchanged.

In batches

Send 1–1,000 creators in a batch. An empty list, too many creators, invalid JSON, or incorrect field types can cause the whole request to fail.

For a valid batch, Viffy saves the valid rows and reports any rejected rows with their reasons.

Request
POST /creators/batch
{
  "creators": [
    { "externalId": "hb-1042", "name": "Jane Doe", "email": "[email protected]" },
    { "externalId": "hb-1043", "name": "Sam Lee", "handle": "@samlee" },
    { "externalId": "", "name": "No id" }
  ]
}
Response
{
  "summary": { "rows": 3, "added": 1, "updated": 1, "unchanged": 0, "rejected": 1 },
  "rows": [
    { "index": 0, "externalId": "hb-1042", "outcome": "updated", "reasons": [] },
    { "index": 1, "externalId": "hb-1043", "outcome": "added", "reasons": [] },
    { "index": 2, "externalId": "", "outcome": "rejected", "reasons": ["This row is missing a Creator ID. Add the ID from your roster."] }
  ]
}

An id repeated within one request is rejected on its second appearance. rows.index counts from 0, in input order. Read summary.rejected and each row’s reasons even after a 200 response; HTTP success does not mean every row succeeded.

Keeping in sync

Send changes from your software with PUT. To collect edits made in Viffy, follow these steps.

  • Call GET /creators?updatedSince= with the UTC start time of your last completed update, and read every page.
  • The filter includes creators whose updatedAt equals that time. Omit active to include deactivated creators.
  • Add or update your saved creators by externalId. Allow overlapping reads so a repeated creator updates the same entry.
  • Periodically compare the full roster. Changes made while you read can move creators between pages.