Inkode integration reference for workspace APIs, Canva, SCIM, and webhooks
Inkode exposes multiple integration surfaces instead of one auth model for every/apiroute. This guide documents the supported surfaces we expose today and clarifies which auth method belongs to each one.
Integrations · Security · Pricing · Compare
Quick start
- Configure Canva OAuth for the Inkode app in the Canva Developer Portal.
- Use the Canva-managed bearer token against the Canva Library API under
/api/canva/*. - Create an optional
ink_API key in Settings > API Keys for direct server-to-server access. - Use SCIM bearer tokens for
/api/scim/v2/{orgSlug}provisioning.
curl "https://inkode.io/api/canva/me" \
-H "Authorization: Bearer oauth_access_token"Authentication
Match the auth method to the route family
Inkode exposes different auth models for different route families. Workspace endpoints use Clerk sessions, Canva endpoints accept OAuth bearer tokens for the Canva app and optional ink_ API keys for direct integrations, and SCIM provisioning uses separate bearer tokens generated in Security Center.
Clerk session
Cookie-based browser auth
Use same-origin requests from a signed-in Inkode workspace for routes such as /api/qr, /api/links, /api/barcode, /api/org/*, and /api/webhooks/endpoints.
Canva OAuth or API key
Authorization: Bearer <oauth-token>, Bearer ink_..., or X-API-Key
The Canva Library API under /api/canva/* accepts OAuth bearer tokens for the Canva app and workspace-scoped ink_ API keys for direct server-to-server access.
SCIM bearer
Authorization: Bearer scim_...
SCIM tokens authenticate the enterprise provisioning endpoints under /api/scim/v2/{orgSlug}.
Workspace API
Same-origin CRUD and analytics endpoints
These routes power the Inkode dashboard. They are suitable for same-origin browser requests or trusted internal automations that carry a valid workspace session cookie. They are not API-key routes.
/api/qr?orgId={orgId}List QR codes with pagination, search, folder filters, tag filters, and summary totals.
/api/qrCreate a QR code. Supports dynamic URL QR creation and Idempotency-Key retries.
/api/qr/{id}Fetch one QR code with linked smart-link metadata when present.
/api/qr/{id}Update content, styling, lifecycle settings, short code, or linked smart link.
/api/qr/{id}Delete a QR code from the authenticated workspace.
/api/qr/{id}/stats?days=30Read QR analytics, A/B metrics, device and country breakdowns, and routing insights.
/api/links?orgId={orgId}List short links with search, folder/tags, `filter` (all, active, inactive, ab, expiring, health_failing, health_monitored), sort, and pagination.
/api/linksCreate a short link, optionally attach a QR code, and support Idempotency-Key retries.
/api/links/{id}Fetch one short link with tags, linked QR code, routing profile, and link-page associations.
/api/links/{id}Update slug, domain, routing rules, UTM fields, password protection, lifecycle settings, or linked assets.
/api/links/{id}Delete a short link from the authenticated workspace.
/api/links/{id}/stats?days=30Read click analytics, referrers, device and country breakdowns, and A/B results.
/api/barcode?orgId={orgId}List barcode assets with search, filters, tags, and pagination.
/api/barcodeCreate a barcode with normalized type, value validation, folder placement, and optional integration binding.
/api/barcode/{id}Fetch a single barcode with creator details and integration link metadata when available.
/api/barcode/{id}Update barcode type, value, style, activity state, folder assignment, or integration mapping.
/api/barcode/{id}Delete a barcode from the workspace.
Important
The QR and short-link create endpoints supportIdempotency-Keyheaders to avoid duplicate records on retried requests.
Example
await fetch("/api/qr?orgId=org_123", {
credentials: "include",
headers: {
"Content-Type": "application/json"
}
})Canva Library API
OAuth- or API-key authenticated reads and PNG renders
The Canva endpoints are CORS-enabled and accept either an OAuth bearer token, an ink_ API key via Authorization, or X-API-Key. They are intended for Canva asset browsing, lightweight integrations, and image export flows.
/api/canva/meResolve the current OAuth token or API key to workspace, user, and connection metadata.
/api/canva/foldersList non-archived folders available in the authorized workspace.
/api/canva/qr?search=&folderId=&limit=List QR codes scoped to the authorized workspace. limit is clamped between 1 and 50.
/api/canva/qr/{qrId}/image?size=1024Render a QR code as PNG. size is clamped between 256 and 2048.
/api/canva/barcode?search=&folderId=&limit=List barcodes scoped to the authorized workspace. limit is clamped between 1 and 50.
/api/canva/barcode/{barcodeId}/image?size=800Render a barcode as PNG. size is clamped between 256 and 2048.
Example
curl "https://inkode.io/api/canva/qr?search=summer&limit=10" \
-H "Authorization: Bearer oauth_access_token"Sample response
{
"items": [
{
"id": "qr_abc123",
"name": "Summer promo",
"type": "URL",
"shortCode": "summer24",
"isDynamic": true,
"folderName": "Campaigns"
}
],
"total": 1
}Public Endpoints
Conversion tracking and protected access helpers
These routes are designed for public pages rather than signed-in dashboards. They do not use API keys. Instead, they accept structured JSON payloads and manage the visitor or protected-access cookies required by the experience.
/api/public/accessValidate a password-protected QR or short link and set the access cookie needed for redirect routes.
/api/public/conversionsRecord a conversion event from a public page and create a visitor cookie when needed.
Example
curl -X POST "https://inkode.io/api/public/conversions" \
-H "Content-Type: application/json" \
-d '{
"orgId": "org_123",
"sourceType": "qr",
"sourceId": "qr_abc123",
"step": "purchase",
"value": 129.99,
"currency": "USD"
}'SCIM 2.0
Provision workspace users and role groups
Use SCIM bearer tokens generated in Security Center. The base path is /api/scim/v2/{orgSlug}. Group resources map to workspace roles, and user group assignments can reference either role ids or role names.
/api/scim/v2/ServiceProviderConfigReturn the SCIM service-provider capabilities supported by Inkode.
/api/scim/v2/SchemasReturn the supported SCIM schemas: User and Group.
/api/scim/v2/Schemas/{schemaId}Fetch a specific schema document.
/api/scim/v2/{orgSlug}/UsersList provisioned users. Supports filter values for userName or externalId equality.
/api/scim/v2/{orgSlug}/UsersCreate or upsert a provisioned user. groups map to workspace role ids or role names.
/api/scim/v2/{orgSlug}/Users/{userId}Fetch one provisioned user.
/api/scim/v2/{orgSlug}/Users/{userId}Replace a provisioned user record.
/api/scim/v2/{orgSlug}/Users/{userId}Patch user fields such as active, emails, displayName, or groups.
/api/scim/v2/{orgSlug}/Users/{userId}Deprovision a user and return 204 on success.
/api/scim/v2/{orgSlug}/GroupsList SCIM groups backed by workspace roles.
/api/scim/v2/{orgSlug}/GroupsCreate a SCIM group by role displayName.
/api/scim/v2/{orgSlug}/Groups/{groupId}Fetch one SCIM group.
/api/scim/v2/{orgSlug}/Groups/{groupId}Patch a group displayName or membership list.
/api/scim/v2/{orgSlug}/Groups/{groupId}Delete a SCIM group and return 204 on success.
Supported user filter
filter=userName eq "person@example.com"orfilter=externalId eq "ext_123"
Example
curl -X POST "https://inkode.io/api/scim/v2/acme/Users" \
-H "Authorization: Bearer scim_your_token" \
-H "Content-Type: application/scim+json" \
-d '{
"userName": "ops@example.com",
"displayName": "Ops Manager",
"active": true,
"groups": [{ "value": "Admin" }]
}'Webhooks
Signed QR lifecycle events
Webhook endpoints are configured in the dashboard and every delivery is signed with the endpoint secret using HMAC-SHA256. Inkode retries failed deliveries up to three times with exponential backoff.
Delivery shape
{
"event": "qr.created",
"payload": {
"id": "qr_abc123"
},
"timestamp": "2026-04-02T18:34:11.000Z"
}Verify the signature
Use the raw request body exactly as received (do not re-serialize JSON). Compare HMAC-SHA256 in hex, prefixed with sha256=.
import crypto from "node:crypto"
/** Return true when X-Inkode-Signature matches the raw POST body. */
export function verifyInkodeWebhookSignature(rawBody, secret, header) {
const value = (header ?? "").trim()
if (!value.toLowerCase().startsWith("sha256=")) return false
const received = value.slice(7)
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody, "utf8")
.digest("hex")
if (received.length !== expected.length) return false
return crypto.timingSafeEqual(
Buffer.from(received, "utf8"),
Buffer.from(expected, "utf8")
)
}Headers
X-Inkode-EventThe event name, such as qr.created or qr.expired.
X-Inkode-SignatureHMAC-SHA256 signature prefixed with sha256=.
X-Inkode-DeliveryThe webhook endpoint id used for delivery tracking.
X-Inkode-AttemptRetry attempt number starting at 1.
Event catalog
Common errors
400 validation failed or a required field is missing.
401 the access token, API key, or session is missing or invalid.
404 the resource, route parameter, or workspace-scoped asset was not found.
409 the request conflicts with an existing resource such as a reused slug or short code.
422 the request is valid but uses an unsupported value such as an unsupported barcode render.
Operations
Scheduled jobs (Vercel Cron)
These routes are public to Clerk but require CRON_SECRET: send Authorization: Bearer <CRON_SECRET>, header x-cron-secret, or query ?secret=. In production, CRON_SECRET must be configured or handlers return 503. Schedules are defined in vercel.json.
/api/cron/notificationsDigest and notification fan-out.
/api/cron/qr-alertsQR alerting sweep.
/api/cron/link-health-checksScheduled link health checks.
/api/cron/weekly-workspace-digestWeekly workspace summary emails.
/api/cron/trial-remindersTrial expiry reminders.
/api/cron/invoice-records-backfillStripe invoice row backfill.
/api/cron/billing-snapshot-syncBilling snapshot synchronization.
/api/cron/dunning-remindersPayment recovery reminders.
/api/cron/marketing-campaignsFire scheduled marketing campaigns.
/api/cron/marketing-send-queueDrain queued marketing sends.
/api/cron/expireDeactivate expired short links and QR codes.
/api/cron/downgrade-auditEnforce plan limits after downgrades; limit-warning emails.
Email marketing
Workspace contacts, campaigns, and POS webhooks
These routes require a signed-in Clerk session and the Integrations-capable plan. Sends use Resend (RESEND_API_KEY) with org/send tags for inbound webhooks. Large broadcasts enqueue in PostgreSQL and drain via /api/cron/marketing-send-queue. Configure RESEND_WEBHOOK_SECRET and point Resend to POST /api/webhooks/resend for bounce and complaint suppression. Clover and Square support OAuth callbacks under /api/integrations/callback/marketing-* (public route; no Clerk required on redirect).
/api/org/{orgId}/marketing/overviewCounts: marketable contacts, POS connections, campaigns, sends (30d), queued sends.
/api/org/{orgId}/marketing/contacts?limit=50&cursor=List marketing contacts; cursor pagination by id.
/api/org/{orgId}/marketing/contactsUpsert a contact (body: email, displayName?, marketingConsent?, consentSource?, phone?).
/api/org/{orgId}/marketing/contacts/{contactId}Update consent, suppression, or profile fields.
/api/org/{orgId}/marketing/campaignsList campaigns.
/api/org/{orgId}/marketing/campaignsCreate a campaign (name, subject, htmlBody, segmentFilter?, scheduledAt?, fromOverride?).
/api/org/{orgId}/marketing/campaigns/{campaignId}/sendEnqueue or send: body { maxRecipients?, sync?: boolean }. Default async queue; sync:true sends the full batch in-request.
/api/org/{orgId}/marketing/pos-connectionsList Clover/Square webhook registrations.
/api/org/{orgId}/marketing/pos-connectionsRegister webhook secrets (squareSignatureKey or cloverSharedSecret) for signature verification.
/api/org/{orgId}/marketing/connect/cloverStart Clover OAuth; returns redirectUrl (CLOVER_APP_ID / CLOVER_APP_SECRET).
/api/org/{orgId}/marketing/square-oauth-appWorkspace Square Developer app (ID, sandbox; secret stored encrypted).
/api/org/{orgId}/marketing/square-oauth-appSave workspace Application ID + secret (optional env fallback).
/api/org/{orgId}/marketing/connect/squareStart Square OAuth; returns redirectUrl (uses workspace DB credentials or env).
/api/integrations/callback/marketing-cloverClover OAuth redirect (public).
/api/integrations/callback/marketing-squareSquare OAuth redirect (public).
/api/org/{orgId}/marketing/pos-connections/{connectionId}/syncBackfill customers from Clover or Square using stored OAuth tokens.
/api/webhooks/marketing/clover/{connectionId}Inbound Clover webhook (Authorization: Bearer shared secret).
/api/webhooks/marketing/square/{connectionId}Inbound Square webhook (x-square-hmacsha256-signature).
/api/public/marketing/unsubscribe?t=One-click unsubscribe (no auth).
/api/cron/marketing-campaignsScheduled campaigns (Authorization: Bearer CRON_SECRET).
/api/cron/marketing-send-queueProcess queued marketing sends (Authorization: Bearer CRON_SECRET).
/api/webhooks/resendResend Svix webhooks: bounce, complaint, failed → suppression + optional send row update.