vlayervlayer docs
Engagement verification

Create a campaign

Check a public post, create a campaign and its challenge from the post URL, then end or delete it.

A campaign carries one challenge: the public Instagram or TikTok post creators engage with. You create both in a single call; the response's challenge.id is the challengeId every later request takes. Concepts explains how they relate.

Campaigns are part of engagement verification, which is available on request. To read a post's comments or metrics without one, use the data lookups.

Check the post

GET/admin/posts/thumbnail?url={postUrl}Data lookup

Confirms a URL points at a post the API can read, and returns its cover image, before anything is created. Nothing is stored.

curl -G {API_BASE_URL}/admin/posts/thumbnail \
  -H "Authorization: Bearer <API_TOKEN>" \
  --data-urlencode "url=https://www.instagram.com/p/ABC123/?igsh=xyz"
{
  "data": {
    "platform": "instagram",
    "contentType": "post",
    "contentId": "ABC123",
    "canonicalUrl": "https://www.instagram.com/p/ABC123/",
    "thumbnailUrl": "https://..."
  }
}

Send canonicalUrl as the challenge's targetUrl. Most posts answer in about a second; a few take up to 40 seconds, so set a generous client timeout. A 404 or 422 means the post cannot be read and the campaign's lookups would fail too; see Data lookup errors.

Create

POST/admin/campaigns
curl -X POST {API_BASE_URL}/admin/campaigns \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "brandId": 1,
    "title": "Summer Challenge 2026",
    "description": "Comment on our Instagram post",
    "pointsReward": 100,
    "challenge": {
      "targetUrl": "https://www.instagram.com/p/ABC123/",
      "instructionText": "Leave a comment with #SummerVibes",
      "action": "comment"
    }
  }'
FieldRequiredNotes
brandIdyesBrand that owns the campaign and receives its webhooks
titleyes
pointsRewardyesWhole number, 1 or more. Points issued on reward.issued. Only used by engagement verification, but required whether or not you use it
challenge.targetUrlyesPublic post URL. The platform, content type, and content id are parsed from it
challenge.actionnocomment (default, Instagram and TikTok) or like (Instagram only)
challenge.instructionTextnoShown to creators by your UI
description, imageUrl, startsAt, endsAtno

Response 201:

{
  "data": {
    "campaign": {
      "id": 1,
      "brandId": 1,
      "title": "Summer Challenge 2026",
      "pointsReward": 100,
      "isActive": true,
      "startsAt": null,
      "endsAt": null,
      "createdAt": "2026-06-01T09:00:00.000Z"
    },
    "challenge": {
      "id": 1,
      "campaignId": 1,
      "platform": "instagram",
      "contentType": "post",
      "contentId": "ABC123",
      "canonicalUrl": "https://www.instagram.com/p/ABC123/",
      "instructionText": "Leave a comment with #SummerVibes"
    }
  }
}

Store data.challenge.id. The challenge's stored comments and metrics, likers and attempts are all addressed by challengeId; none of those routes take a URL or shortcode.

Set challenge.action to "like" only for Instagram posts. Liker fetching is not available for TikTok, so a like challenge on a TikTok URL cannot be verified.

Read

GET /campaigns lists campaigns; GET /campaigns/{id} returns one with its challenge.

The list is not the create response reshaped. Each row flattens the campaign, adds brandName and brandLogoUrl, nests a trimmed challenge (id, platform, contentType, canonicalUrl, instructionText), and adds myAttempt — the calling token's own attempt on that challenge, or null. It carries no isActive, because only active campaigns are listed; use GET /campaigns/{id} for one that has ended.

End

POST/admin/campaigns/{id}/end

Sets isActive to false and endsAt to now (an earlier endsAt is kept). Attempts and rewards are preserved. Calling it again is a no-op.

{ "data": { "id": 1, "isActive": false, "endsAt": "2026-07-01T00:00:00.000Z" } }

Delete

DELETE/admin/campaigns/{id}

Hard-deletes the campaign, its challenge, and every stored comment and metrics snapshot. Allowed only while the challenge has zero attempts; otherwise the call returns 409 and you should end the campaign instead.

StatusMeaning
200Deleted. Body: { "data": { "id": 1 } }
404Campaign not found
409 CAMPAIGN_HAS_ACTIVITYCampaign has attempts. Use end

Last updated on