API reference
Campaign ingestion endpoints
Create one card per request from your own JSON shape, using a template and mapping configured once in the dashboard.
- GET
/campaignsList campaigns - GET
/campaigns/:campaignIdRetrieve a campaign - GET
/campaigns/:campaignId/schemaGet a campaign’s payload schema - POST
/campaigns/:campaignId/cards/testDry-run a campaign card - POST
/campaigns/:campaignId/cardsCreate a card in a campaign
List campaigns
Returns the account’s digital business card campaigns.
API key required. Scope: cards:read. A campaign-scoped key may call it for its own campaign. Plan: Pro, or the Business card plan.
Request
| Header | Required | Value |
|---|---|---|
Authorization | Required | Bearer YOUR_API_KEY. X-API-Key: YOUR_API_KEY is accepted instead. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | Optional | Items per page, 1 to 100. Default: 20. |
offset | integer | Optional | How many items to skip. Default: 0. |
Example request
curl "https://api.qrbold.com/api/public/v1/campaigns" \
-H "Authorization: Bearer $QRBOLD_API_KEY"Response
200 A list envelope of campaign objects. A campaign-scoped key sees only its own campaign.
{
"object": "list",
"data": [
{
"object": "campaign",
"id": "cmf3k0b5q0004",
"name": "New starters 2026",
"status": "active",
"apiEnabled": true,
"templateId": "cmf3k1p7d0002",
"createdAt": "2026-09-15T11:00:00.000Z"
}
],
"pagination": {
"total": 1,
"limit": 20,
"offset": 0,
"hasMore": false
}
}Errors
Every endpoint can also answer 401 invalid_api_key, 403 forbidden, 403 upgrade_required_api, 404 unknown_endpoint, 429 rate limited and 500 internal_error. One failing response is shown below.
{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "Invalid API key"
}
}Related
Retrieve a campaign
Returns one campaign, including whether its API is switched on.
API key required. Scope: cards:read. A campaign-scoped key may call it for its own campaign. Plan: Pro, or the Business card plan.
Request
| Header | Required | Value |
|---|---|---|
Authorization | Required | Bearer YOUR_API_KEY. X-API-Key: YOUR_API_KEY is accepted instead. |
| Name | Type | Required | Description |
|---|---|---|---|
campaignId | string | Required | The campaign’s id. Shown on the campaign’s API tab in the dashboard and returned by GET /campaigns. |
Example request
curl "https://api.qrbold.com/api/public/v1/campaigns/cmf3k0b5q0004" \
-H "Authorization: Bearer $QRBOLD_API_KEY"Response
200 The campaign object.
{
"object": "campaign",
"id": "cmf3k0b5q0004",
"name": "New starters 2026",
"status": "active",
"apiEnabled": true,
"templateId": "cmf3k1p7d0002",
"createdAt": "2026-09-15T11:00:00.000Z"
}Errors
| Status | Code | When | Retry? |
|---|---|---|---|
404 | resource_not_found | Nothing with that id exists in this account. | No |
403 | key_scoped_to_campaign | A campaign-scoped key was used somewhere it is not allowed. | Only after changing the request or the account |
Every endpoint can also answer 401 invalid_api_key, 403 forbidden, 403 upgrade_required_api, 404 unknown_endpoint, 429 rate limited and 500 internal_error. One failing response is shown below.
{
"error": {
"type": "not_found",
"code": "resource_not_found",
"message": "Card not found"
}
}Related
Get a campaign’s payload schema
Returns the JSON shape this campaign accepts, derived from its template, with a ready-to-post sample.
API key required. Scope: cards:read. A campaign-scoped key may call it for its own campaign. Plan: Pro, or the Business card plan.
Request
| Header | Required | Value |
|---|---|---|
Authorization | Required | Bearer YOUR_API_KEY. X-API-Key: YOUR_API_KEY is accepted instead. |
| Name | Type | Required | Description |
|---|---|---|---|
campaignId | string | Required | The campaign’s id. Shown on the campaign’s API tab in the dashboard and returned by GET /campaigns. |
Example request
curl "https://api.qrbold.com/api/public/v1/campaigns/cmf3k0b5q0004/schema" \
-H "Authorization: Bearer $QRBOLD_API_KEY"Response
200 A campaign_schema object.
{
"object": "campaign_schema",
"campaignId": "cmf3k0b5q0004",
"payloadMode": "native",
"templateId": "cmf3k1p7d0002",
"fingerprint": "9f3a1c22",
"activatedFingerprint": "9f3a1c22",
"stale": false,
"fields": [
{
"key": "firstName",
"label": "First name",
"required": true
}
],
"sections": [
{
"key": "gallery",
"label": "Gallery",
"kind": "list",
"max": 24,
"props": [
{
"prop": "url",
"label": "Photo"
}
]
}
],
"blocks": [
{
"id": "heading-1",
"type": "heading",
"props": [
{
"prop": "text",
"value": "Welcome"
}
]
}
],
"sample": {
"externalId": "EMP-1001",
"firstName": "Ada",
"lastName": "Lovelace",
"gallery": []
}
}Errors
| Status | Code | When | Retry? |
|---|---|---|---|
404 | resource_not_found | Nothing with that id exists in this account. | No |
403 | key_scoped_to_campaign | A campaign-scoped key was used somewhere it is not allowed. | Only after changing the request or the account |
Every endpoint can also answer 401 invalid_api_key, 403 forbidden, 403 upgrade_required_api, 404 unknown_endpoint, 429 rate limited and 500 internal_error. One failing response is shown below.
{
"error": {
"type": "not_found",
"code": "resource_not_found",
"message": "Card not found"
}
}Good to know
- stale: true means the template changed after the configuration was activated. Requests still work; re-read the schema.
Related
Dry-run a campaign card
Runs a payload through the campaign exactly as the real endpoint would, and creates nothing.
API key required. Scope: cards:write. A campaign-scoped key may call it for its own campaign. Plan: Pro, or the Business card plan.
Request
| Header | Required | Value |
|---|---|---|
Authorization | Required | Bearer YOUR_API_KEY. X-API-Key: YOUR_API_KEY is accepted instead. |
Content-Type | Required | application/json |
| Name | Type | Required | Description |
|---|---|---|---|
campaignId | string | Required | The campaign’s id. Shown on the campaign’s API tab in the dashboard and returned by GET /campaigns. |
Request body
The same body you would send to the real endpoint: your own JSON in mapped mode, a card document in native mode.
Example request
curl -X POST "https://api.qrbold.com/api/public/v1/campaigns/cmf3k0b5q0004/cards/test" \
-H "Authorization: Bearer $QRBOLD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"employee": {
"id": "EMP-1001",
"name": {
"first": "Ada",
"last": "Lovelace"
},
"contact": {
"email": "ada@example.com"
}
}
}'Response
200 A dry_run object. The status is 200 whether or not the payload would be accepted; read ok.
{
"object": "dry_run",
"ok": true,
"externalId": "EMP-1001",
"resolvedFields": {
"firstName": "Ada",
"lastName": "Lovelace",
"emails.0.value": "ada@example.com"
},
"unmappedPaths": [],
"shortCode": null,
"warnings": [],
"errors": []
}Errors
| Status | Code | When | Retry? |
|---|---|---|---|
403 | campaign_api_disabled | The campaign is not accepting API requests. | Only after changing the request or the account |
409 | api_not_configured | The campaign has never had an API configuration activated. | Only after changing the request or the account |
400 | invalid_json_structure | The body is valid JSON but not an object. | Only after changing the request or the account |
404 | resource_not_found | Nothing with that id exists in this account. | No |
403 | key_scoped_to_campaign | A campaign-scoped key was used somewhere it is not allowed. | Only after changing the request or the account |
Every endpoint can also answer 401 invalid_api_key, 403 forbidden, 403 upgrade_required_api, 404 unknown_endpoint, 429 rate limited and 500 internal_error. One failing response is shown below.
{
"error": {
"type": "permission_error",
"code": "campaign_api_disabled",
"message": "The API is turned off for this campaign."
}
}Related
Create a card in a campaign
Creates one card from a single record, using the campaign’s template, mapping and short-link rule.
API key required. Scope: cards:write. A campaign-scoped key may call it for its own campaign. Plan: Pro, or the Business card plan.
Request
| Header | Required | Value |
|---|---|---|
Authorization | Required | Bearer YOUR_API_KEY. X-API-Key: YOUR_API_KEY is accepted instead. |
Content-Type | Required | application/json |
Idempotency-Key | Optional | Any unique value up to 255 characters. Makes a retry safe. See idempotency. |
| Name | Type | Required | Description |
|---|---|---|---|
campaignId | string | Required | The campaign’s id. Shown on the campaign’s API tab in the dashboard and returned by GET /campaigns. |
Request body
One JSON object, up to 1 MB, creates one card. In mapped mode it is your own record, unchanged. In native mode it is a card document whose top-level externalId is required. Do not send templateId: the campaign owns it.
Example request
curl -X POST "https://api.qrbold.com/api/public/v1/campaigns/cmf3k0b5q0004/cards" \
-H "Authorization: Bearer $QRBOLD_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"employee": {
"id": "EMP-1001",
"name": {
"first": "Ada",
"last": "Lovelace"
},
"contact": {
"email": "ada@example.com",
"mobile": "+44 20 7946 0958"
}
}
}'Response
201 The card object, with externalId set to your identifier. The response carries a Request-Id header.
{
"object": "card",
"id": "cmf3k2a9x0001",
"name": "Ada Lovelace",
"shortCode": "k3Vq8ZtB",
"status": "published",
"url": "https://qrbold.com/c/k3Vq8ZtB",
"vcardUrl": "https://qrbold.com/c/k3Vq8ZtB/vcard",
"templateId": "cmf3k1p7d0002",
"externalId": "EMP-1001",
"paused": false,
"fields": {
"firstName": "Ada",
"lastName": "Lovelace",
"title": "Head of Engineering",
"company": "Analytical Engines Ltd",
"photoUrl": "https://example.com/photos/ada.jpg",
"companyLogoUrl": "https://example.com/brand/logo.png",
"bio": "Builds calculating machines and the teams around them.",
"emails": [
{
"label": "Work",
"value": "ada@example.com"
}
],
"phones": [
{
"label": "Mobile",
"value": "+44 20 7946 0958"
}
],
"links": [
{
"type": "website",
"url": "example.com"
},
{
"type": "linkedin",
"url": "linkedin.com/in/ada-lovelace"
}
],
"address": {
"city": "London",
"country": "United Kingdom"
}
},
"createdAt": "2026-10-09T09:30:00.000Z",
"updatedAt": "2026-10-09T09:30:00.000Z"
}Errors
| Status | Code | When | Retry? |
|---|---|---|---|
409 | duplicate_external_id | A card with that identifier already exists in the campaign. | No |
409 | duplicate_in_trash | A card with that identifier is sitting in the account’s Trash. | Only after changing the request or the account |
409 | short_link_already_exists | The custom short link the campaign resolved from the payload is taken. | Only after changing the request or the account |
422 | missing_external_id | The payload did not carry your identifier for the person. | Only after changing the request or the account |
422 | invalid_external_id | The identifier is not usable. | Only after changing the request or the account |
422 | invalid_short_link | The custom short link in a campaign payload is not valid. | Only after changing the request or the account |
422 | invalid_payload | A native-mode campaign payload does not match the template’s schema. | Only after changing the request or the account |
422 | validation_failed | The card’s template does not allow what was sent. | Only after changing the request or the account |
403 | campaign_api_disabled | The campaign is not accepting API requests. | Only after changing the request or the account |
409 | api_not_configured | The campaign has never had an API configuration activated. | Only after changing the request or the account |
400 | invalid_json | The request body is not valid JSON. | Only after changing the request or the account |
400 | invalid_json_structure | The body is valid JSON but not an object. | Only after changing the request or the account |
400 | payload_too_large | The request body is over the size limit. | Only after changing the request or the account |
403 | limit_exhausted_cards | The account has used every card its plan covers. | Only after changing the request or the account |
404 | resource_not_found | Nothing with that id exists in this account. | No |
403 | key_scoped_to_campaign | A campaign-scoped key was used somewhere it is not allowed. | Only after changing the request or the account |
409 | idempotency_key_reused | This Idempotency-Key was already used with a different request. | Only after changing the request or the account |
Every endpoint can also answer 401 invalid_api_key, 403 forbidden, 403 upgrade_required_api, 404 unknown_endpoint, 429 rate limited and 500 internal_error. One failing response is shown below.
{
"error": {
"type": "conflict",
"code": "duplicate_external_id",
"message": "A card with external id \"EMP-1001\" already exists in this campaign."
}
}Good to know
- Your identifier is unique inside the campaign, so a repeated export cannot create a second card for the same person.