VCode integrates through the VPlatform Client API. The API is small and the endpoints are unsurprising. What trips people up is the object model, so that is what this page leads with.
The one thing to understand. A VCode does not contain a behaviour. It points at one or more packages. A package is a pairing of one action (what should happen) with any number of rules (when it should happen). Get that right and the rest of the API is CRUD.
How resolution works
The object model
| Object | What it is | Endpoint |
|---|---|---|
| Action | What happens on a successful scan. Open a URL, return text, hand over a contact card, deep link into an app, return true or false, or something custom. | /actions |
| Rule | A condition tested at scan time. Location, date and time, platform, single scan, email, invalidate, boolean. | /rules |
| Package | One action plus the rules that gate it. This is the unit you attach to a code. | /packages |
| VCode | The identity itself. Carries package IDs, an optional scan budget and an optional expiry date. | /vcodes |
A code with two packages can behave two ways from one printed symbol, because each package carries its own rules and the platform picks the one whose conditions are met.
Authentication
Two schemes are supported. Server-to-server integrations use an API key header:
X-VPLATFORM-API-KEY: your-key
User-context calls use a bearer token obtained from the login endpoint, which returns an accessToken and a refreshToken. Refresh through POST /auth/refresh rather than logging in again.
curl -X POST https://api.vplatform.io/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"..."}'
Quickstart: a working VCode in two calls
There is a shortcut endpoint that creates the action, the package and the code in one request. Use it to prove the integration before building anything.
curl -X POST https://api.vplatform.io/vcodes/create-url \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json' \
-d '{
"title": "First code",
"description": "Proving the integration",
"url": "https://example.com/hello",
"inapp_browser": true
}'
The response carries the code’s uti, which is its identifier. Fetch the symbol itself as PNG or SVG:
GET https://api.vplatform.io/vcodes/{uti}/png
GET https://api.vplatform.io/vcodes/{uti}/svg
Print it, scan it, and you have an end-to-end integration. Everything below is refinement.
The full sequence
When you need rules, build the objects in dependency order.
1. Create the action. The data object is discriminated on type.
POST /actions
{
"title": "Open the product page",
"data": { "type": "url", "url": "https://example.com/p/123", "inapp_browser": true }
}
2. Create the rules. Same pattern, discriminated on type.
POST /rules
{
"title": "Venue only",
"description": "Resolve inside the boundary",
"data": { "type": "geofence", "geofenceType": "radius",
"latitude": 53.4808, "longitude": -2.2426, "radius": 250 }
}
3. Create the package, pairing the action with the rules.
POST /packages
{
"title": "Gate entry",
"description": "Entry, inside the venue only",
"actionId": "<action id>",
"rules": [ "<rule id>" ]
}
4. Create the VCode and attach the package.
POST /vcodes
{
"title": "Seat C12-8",
"packageIds": [ "<package id>" ],
"allowHistory": true,
"expireAfterScans": 1
}
What a scan can return
Action types, by the type discriminator:
url, open a destination, optionally in an in-app browsertextanddynamic_text, return content directlycontact_card, return contact detailsapp_link, deep link into an applicationtrue_false, return a boolean, which is the one to use for an authenticity checknftcustom, return your own payload for your application to interpret
Rules
Rule types, by the type discriminator:
geofence, by radius or polygondatetime, start and end dates plus hours and minutes of the dayplatform, one ofall,ios,android,websingle_scan, with options fromONCE_ONLYthrough per user, per device, per day, week, month and yearemail, allow and block lists by address or domaininvalidate, for named user IDsboolean, matched on a value your integration supplies
Most rules also accept an inverseAction, which is how one code returns a different response instead of simply refusing.
Resolving a scan
Two endpoints, and the distinction matters. POST /scan is a scan by a camera and carries the context the rules need. POST /open is a lookup by UTI without a scan event.
POST /scan
{
"uti": "<uti>",
"userId": "<user id>",
"deviceId": "<device id>",
"latitude": 53.4808,
"longitude": -2.2426,
"platform": "ios"
}
Send the context you have. Omitting latitude and longitude means a geofence rule cannot be satisfied, and omitting userId means holder rules cannot be.
History and statistics
GET /historyandGET /history/{id}for the scan record.GET /vcodes/{id}/statisticsfor totals per code, including unique scans, unique locations, unique users and devices, and the iOS and Android split.PUT /vcodes/{id}/stateto revoke or reinstate a code after it is in the field.
Full reference
The complete interactive definition, covering all endpoints and schemas, is published as OpenAPI:
Codes, rules, actions and packages can also be managed through the VPlatform portal at portal.vplatform.io if you would rather click than curl while you are learning the model.
Want credentials?
Tell us what you are building. API access and SDK access are provided to approved partners.
Security and procurement questions are answered on the Trust Centre.
