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
externalIdin the path is yours. Repeating the call with the same id updates the creator without creating a duplicate. emailandhandle: leave a field out to keep it, sendnullto clear it.activedefaults to true for a new creator.falseprevents 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.
| Field | Rule |
|---|---|
externalId | Required. One line, up to 100 characters. Cannot change. |
name | Required for a new creator; omit or null keeps the existing name on an update. Nonblank, one line, up to 200 characters. |
email | Optional. Valid email address, up to 254 characters. null or empty clears it. |
handle | Optional. One line, up to 100 characters. null or empty clears it. |
Response fields
| Field | What it is |
|---|---|
externalId / name | Strings: 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 / handle | String or null when not set. |
active | Boolean roster status, separate from each campaign link’s active status. |
createdAt / updatedAt | UTC 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
updatedAtequals that time. Omitactiveto 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.