vlayer logovlayer
API reference

Challenge data

Comments, likers, post metrics, and attempt administration for a challenge

List stored comments

Get paginated comments stored for a challenge post.

GET
/admin/challenges/{challengeId}/comments

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger

Query Parameters

pageinteger
Default: 1
limitinteger
Default: 20
curl -X GET "//admin/challenges/{challengeId}/comments" \
  -H "Authorization: Bearer <token>"

Comment list

{
  "data": [
    {
      "id": 1,
      "platformCommentId": "17890012345678",
      "username": "creator_jane",
      "commentText": "Love this product!",
      "commentedAt": "2019-08-24T14:15:22Z",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 150
  }
}

Get latest post metrics

Returns the most recent metrics snapshot for the challenge post (likes, comments, shares, views).

GET
/admin/challenges/{challengeId}/metrics

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
curl -X GET "//admin/challenges/{challengeId}/metrics" \
  -H "Authorization: Bearer <token>"

Latest metrics

{
  "data": {
    "likeCount": 5432,
    "commentCount": 312,
    "shareCount": 89,
    "viewCount": 125000,
    "fetchedAt": "2019-08-24T14:15:22Z"
  }
}

Force-refresh post metrics

Fetches fresh metrics (Instagram post info or TikTok video info) and stores a new snapshot. Use this to get up-to-date like/comment/share/view counts.

If the scraping provider cannot read the post, the reason is propagated — see the ScrapingError schema. The most common case is an age-restricted Instagram post (422 POST_AGE_RESTRICTED), which can only be resolved by the creator reposting without the restriction.

POST
/admin/challenges/{challengeId}/metrics/refresh

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
curl -X POST "//admin/challenges/{challengeId}/metrics/refresh" \
  -H "Authorization: Bearer <token>"

Freshly fetched metrics

{
  "data": {
    "likeCount": 5432,
    "commentCount": 312,
    "shareCount": 89,
    "viewCount": 125000,
    "fetchedAt": "2019-08-24T14:15:22Z"
  }
}

Force-refresh comments

Fetches all comments (up to 500 pages) and Instagram threaded replies. Inserts new comments into the database (deduplicates by platform comment ID). Use this to pull the latest comments for verification.

If the scraping provider cannot read the post, the reason is propagated — see the ScrapingError schema.

POST
/admin/challenges/{challengeId}/comments/refresh

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
curl -X POST "//admin/challenges/{challengeId}/comments/refresh" \
  -H "Authorization: Bearer <token>"

Refresh result

{
  "data": {
    "totalFetched": 347
  }
}

Create attempt for a user (admin)

Create an attempt on behalf of a specific creator. Caller must be admin. Identify the creator by either userId (internal integer id) or externalId (customer-side identifier stored on users.externalId).

POST
/admin/challenges/{challengeId}/attempts

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Request Body

application/jsonRequired
userIdinteger

Internal user id. Mutually exclusive with externalId — provide one.

externalIdstring

Customer-side identifier stored on users.externalId. Mutually exclusive with userId — provide one.

Path Parameters

challengeIdRequiredinteger
curl -X POST "//admin/challenges/{challengeId}/attempts" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 42,
    "externalId": "cust_abc123"
  }'

Attempt created

{
  "data": {
    "id": 1,
    "userId": 42,
    "challengeId": 1,
    "state": "created",
    "retryCount": 0,
    "maxRetries": 10,
    "lastVerificationAt": "2019-08-24T14:15:22Z",
    "verificationDeadline": "2019-08-24T14:15:22Z",
    "stateHistory": [
      {
        "state": "string",
        "at": "2019-08-24T14:15:22Z",
        "reason": "string"
      }
    ],
    "metadata": {},
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z"
  }
}

Get attempt by ID

GET
/admin/challenges/{challengeId}/attempts/{attemptId}

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
attemptIdRequiredinteger
curl -X GET "//admin/challenges/{challengeId}/attempts/{attemptId}" \
  -H "Authorization: Bearer <token>"

Attempt details

{
  "data": {
    "id": 1,
    "state": "verified",
    "retryCount": 0,
    "maxRetries": 10,
    "matchedComment": {
      "commentText": "Love this product! #SummerVibes",
      "commentedAt": "2019-08-24T14:15:22Z"
    },
    "pointsAwarded": 100,
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z"
  }
}

Progress attempt for a user (admin)

Advance an attempt's state on behalf of the attempt's owning creator. Caller must be admin. The owning user is resolved from the attempt row — no userId/externalId needed in the body.

PATCH
/admin/challenges/{challengeId}/attempts/{attemptId}

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Request Body

application/jsonRequired
actionRequiredstring
Value in: "target_opened" | "claim_completion"

Path Parameters

challengeIdRequiredinteger
attemptIdRequiredinteger
curl -X PATCH "//admin/challenges/{challengeId}/attempts/{attemptId}" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "target_opened"
  }'

Attempt updated

{
  "data": {
    "id": 1,
    "state": "target_opened"
  }
}

Force-verify attempt

Manually transitions a stuck attempt to verified state, schedules the reward, and dispatches the creator.verified webhook (followed by reward.issued once the reward is issued). Works on any non-terminal, non-verified, non-rewarded attempt.

POST
/admin/challenges/{challengeId}/attempts/{attemptId}/force-verify

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
attemptIdRequiredinteger
curl -X POST "//admin/challenges/{challengeId}/attempts/{attemptId}/force-verify" \
  -H "Authorization: Bearer <token>"

Attempt force-verified

{
  "data": {
    "id": 0,
    "state": "verified",
    "previousState": "verification_pending"
  }
}

Redeliver outbound webhooks for attempt

Re-sends outbound webhooks (creator.verified, reward.issued, verification.failed) for an attempt. Use after a receiver outage to replay deliveries that exhausted their delivery retries. If events is omitted, every event eligible for the attempt's current state is redelivered (rewarded → both verified + reward; verified → verified only; failed_* → verification.failed). Caller must be admin.

POST
/admin/challenges/{challengeId}/attempts/{attemptId}/redeliver-webhooks

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Request Body

application/jsonOptional
eventsarray<string>

Path Parameters

challengeIdRequiredinteger
attemptIdRequiredinteger
curl -X POST "//admin/challenges/{challengeId}/attempts/{attemptId}/redeliver-webhooks" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      "creator.verified"
    ]
  }'

Redelivery enqueued

{
  "data": {
    "attemptId": 0,
    "redelivered": [
      "string"
    ]
  }
}

Get attempt by social handle

Look up a creator's attempt for a challenge by their social media handle (e.g. creator_jane or @creator_jane). Returns null if no attempt found.

GET
/admin/challenges/{challengeId}/attempts/by-handle/{handle}

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
handleRequiredstring
curl -X GET "//admin/challenges/{challengeId}/attempts/by-handle/creator_jane" \
  -H "Authorization: Bearer <token>"

Attempt for this handle (or null)

{
  "data": {
    "id": 1,
    "state": "verified",
    "retryCount": 0,
    "maxRetries": 10,
    "matchedComment": {
      "commentText": "Love this product! #SummerVibes",
      "commentedAt": "2019-08-24T14:15:22Z"
    },
    "pointsAwarded": 100,
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z"
  }
}

Fetch likers for a challenge's Instagram post

Starts an asynchronous fetch of the likers of the Instagram post this challenge already references — the post is derived from the challenge, so no shortcode/URL is supplied. Returns 202 immediately; poll the status endpoint for progress.

Coverage caveat: this uses a public scraper, so the number of fetched liker profiles may be lower than Instagram's visible like count. Only supported for Instagram challenges.

POST
/admin/challenges/{challengeId}/likers/refresh

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger

Query Parameters

maxCountinteger

Max likers to fetch. Defaults to 1000.

Default: 1000Minimum: 1Maximum: 5000
curl -X POST "//admin/challenges/{challengeId}/likers/refresh" \
  -H "Authorization: Bearer <token>"

Fetch accepted and started

{
  "data": {
    "challengeId": 1,
    "status": "succeeded",
    "fetchedCount": 428,
    "totalLikes": 432,
    "errorMessage": "string",
    "startedAt": "2019-08-24T14:15:22Z",
    "finishedAt": "2019-08-24T14:15:22Z",
    "requestedAt": "2019-08-24T14:15:22Z",
    "warning": "string"
  }
}

Status of the latest likers fetch for a challenge

GET
/admin/challenges/{challengeId}/likers/status

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger
curl -X GET "//admin/challenges/{challengeId}/likers/status" \
  -H "Authorization: Bearer <token>"

Latest fetch status, or null if none has been requested

{
  "data": {
    "challengeId": 1,
    "status": "succeeded",
    "fetchedCount": 428,
    "totalLikes": 432,
    "errorMessage": "string",
    "startedAt": "2019-08-24T14:15:22Z",
    "finishedAt": "2019-08-24T14:15:22Z",
    "requestedAt": "2019-08-24T14:15:22Z",
    "warning": "string"
  }
}

List stored likers for a challenge (paginated)

Returns likers from the most recent completed fetch for the challenge. Empty if no fetch has succeeded yet.

GET
/admin/challenges/{challengeId}/likers

Authorization

AuthorizationRequiredBearer <token>

API token. Send it as Authorization: Bearer <API_TOKEN>.

In: header

Path Parameters

challengeIdRequiredinteger

Query Parameters

pageinteger
Default: 1
limitinteger
Default: 20Maximum: 100
curl -X GET "//admin/challenges/{challengeId}/likers" \
  -H "Authorization: Bearer <token>"

Liker rows

{
  "data": [
    {
      "id": 1,
      "username": "jane_doe",
      "fullName": "Jane Doe",
      "igUserId": "1784500123",
      "isPrivate": true,
      "isVerified": true,
      "profilePicUrl": "string",
      "likedPost": true,
      "postUrl": "string",
      "totalLikes": 0
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 150
  },
  "warning": "string"
}