API reference
Use the HTTPS API reference for link creation, slug checks, expirations, routing, conversions, QR workflows, analytics, and integrations.
Prerequisites
- An active Nimriz workspace.
- A Workspace API Key generated from IntegrationsDeveloperBackend API access in the dashboard. This key is used for all link-management API calls.
- A secure server-side environment. Never expose your API key in browser-side code, mobile apps, or public repositories.
- For conversion tracking: a separate Conversion API signing secret (also in Integrations). This secret is used exclusively for the Conversion API and is not the same as the workspace API key.
Fundamental rules
- All write operations (creating links, updating slugs, changing passwords) must come from a trusted server backend.
- Link uniqueness is domain-bound:
(domain_id, short_code)must be unique, not justshort_codeglobally. - Redirects default to
302. Only request301when you explicitly want a permanent redirect-browsers cache 301s aggressively. - Host matching is strict.
example.comandwww.example.comare completely separate domains in Nimriz.
Canonical hosts
| Purpose | Host |
|---|---|
| Browser dashboard and auth routes | https://app.nimriz.com |
| Machine-facing API traffic | https://api.nimriz.com |
| Redirect traffic | Your short domain (e.g., https://links.example.com) |
Use api.nimriz.com for all programmatic link-management calls. Short-link domains are redirect surfaces-do not use them as the long-term machine API host.
Authentication
Every link-management API call requires a workspace API key in one of these headers:
Authorization: Bearer <WORKSPACE_API_KEY>
X-Nim-Api-Key: <WORKSPACE_API_KEY>
Both formats are supported. The Authorization: Bearer style is preferred for consistency with standard HTTP auth patterns.
The same workspace API key authenticates the MCP server, which exposes link, analytics, and delivery-health tools to MCP-compatible AI clients.
The Conversion API uses a different secret. Do not use your workspace API key for Conversion API requests.
Resource identifiers
| Identifier | Type | Description |
|---|---|---|
domain_id | UUID | The target short-link domain. |
url_id | UUID | The created short link. Use this as your durable reference-slugs can change, but url_id is permanent. |
short_code | String | The slug path segment (e.g., spring-launch). |
short_url | String | The full short URL (e.g., https://links.example.com/spring-launch). |
POST /api/shorten-Create a link
Creates a single short link.
POST https://api.nimriz.com/api/shorten
Authorization: Bearer <WORKSPACE_API_KEY>
Content-Type: application/json
Request body:
{
"domain_id": "00000000-0000-0000-0000-000000000000",
"long_url": "https://example.com/landing?utm_source=twitter&utm_medium=social&utm_campaign=spring",
"custom_slug": "spring-launch",
"expires_at": "2026-12-31T23:59:59Z",
"password": "optional-secret",
"redirect_status_code": 302
}
| Field | Required | Description |
|---|---|---|
domain_id | Yes | UUID of the target domain. |
long_url | Yes | The destination URL. Must use https:// (or http:// if the domain policy allows). |
custom_slug | No | Your chosen slug. If omitted, Nimriz generates a random one. |
expires_at | Conditional | ISO 8601 timestamp. Required by some domain policies. |
password | No | Plain-text password for password protection (plan-gated). |
redirect_status_code | No | 302 (default) or 301. Avoid 301 unless the destination is truly permanent. |
Successful response:
{
"url_id": "22222222-2222-2222-2222-222222222222",
"short_code": "spring-launch",
"short_url": "https://links.example.com/spring-launch",
"expires_at": "2026-12-31T23:59:59.000Z",
"password_protected": false
}
POST /api/shorten/bulk-Bulk create links
Creates up to 25 links in a single request. Designed for imports and automation jobs that require idempotent retries.
POST https://api.nimriz.com/api/shorten/bulk
Authorization: Bearer <WORKSPACE_API_KEY>
Content-Type: application/json
Request body:
{
"domain_id": "00000000-0000-0000-0000-000000000000",
"items": [
{
"client_row_id": "row-1",
"idempotency_key": "import-session-abc-row-1",
"long_url": "https://example.com/a",
"custom_slug": "campaign-a"
},
{
"client_row_id": "row-2",
"idempotency_key": "import-session-abc-row-2",
"long_url": "https://example.com/b"
}
]
}
| Field | Required | Description |
|---|---|---|
domain_id | Yes | Applies to all items in the batch. |
items[].client_row_id | Yes | Your stable identifier for this row within the batch. Returned in the response so you can match results to inputs. |
items[].idempotency_key | Yes | A stable key for this exact mutation. If you replay the same key, Nimriz returns the stored result instead of creating a duplicate. |
items[].long_url | Yes | Destination URL for this row. |
items[].custom_slug | No | Custom slug for this row. |
items[].expires_at | No | Per-row expiration. |
Rules:
- Maximum 25 items per request.
- Validation failures are returned per-row-a single row failing does not fail the entire batch.
- Replaying the same
idempotency_keyreturns the stored result withidempotent: true.
Successful response:
{
"results": [
{
"client_row_id": "row-1",
"ok": true,
"idempotent": false,
"url_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"short_code": "campaign-a",
"short_url": "https://links.example.com/campaign-a"
},
{
"client_row_id": "row-2",
"ok": false,
"error": "Slug is reserved",
"code": "slug_reserved"
}
]
}
POST /api/check-slug-Check slug availability
Validates whether a candidate slug is available on a specific domain before attempting creation.
{
"domain_id": "00000000-0000-0000-0000-000000000000",
"slug": "spring-launch"
}
Available:
{ "available": true, "min_length": 5 }
Not available:
{
"available": false,
"error": "Slug is reserved",
"code": "slug_reserved",
"min_length": 5
}
A slug that is already taken returns "Slug already exists" (HTTP 409, no code field); a recently deleted slug returns "code": "slug_tombstoned" (HTTP 409).
PUT /api/update-slug-Update a slug
Changes the slug on an existing link. The old slug stops working immediately after the update.
{
"url_id": "22222222-2222-2222-2222-222222222222",
"new_slug": "spring-launch-v2"
}
Response:
{
"short_code": "spring-launch-v2",
"short_url": "https://links.example.com/spring-launch-v2"
}
PUT /api/update-expiration-Update or remove expiration
Sets, changes, or removes the expiration time on a link.
{
"url_id": "22222222-2222-2222-2222-222222222222",
"expires_at": "2026-12-31T23:59:59Z"
}
To remove expiration (when policy allows):
{
"url_id": "22222222-2222-2222-2222-222222222222",
"expires_at": null
}
Response:
{
"url_id": "22222222-2222-2222-2222-222222222222",
"expires_at": "2026-12-31T23:59:59.000Z",
"was_expired": false
}
PUT /api/update-password-Set or remove password protection
Set or update a password:
{
"url_id": "22222222-2222-2222-2222-222222222222",
"password": "new-password"
}
Remove password protection:
{
"url_id": "22222222-2222-2222-2222-222222222222",
"password": null
}
Routing rules
Routing rules (geo and device targeting, A/B splits, deep links) are managed from the dashboard link editor and are not part of the workspace-API-key surface. The dashboard saves the complete ordered ruleset in one operation-the first matching enabled rule wins at redirect time, and the supported action types are destination_override, ab_split, and deep_link. See Routing rules and preview.
POST /api/conversions/callback/:workspace_id-Record a conversion
Sends a signed conversion event from your backend.
POST https://api.nimriz.com/api/conversions/callback/<WORKSPACE_ID>
Content-Type: application/json
X-Nim-Timestamp: <unix-seconds>
X-Nim-Signature: v1=<hex-hmac-sha256>
Idempotency-Key: <stable-key>
The signature is computed as HMAC-SHA256("${X-Nim-Timestamp}.${raw_request_body}", CONVERSION_SECRET).
Example request body:
{
"event_name": "sale",
"event_time": "2026-03-26T12:00:00.000Z",
"event_id": "evt_sale_123",
"user_data": {
"click_id": "nimct_exampletoken",
"external_id": "cust_123"
},
"custom_data": {
"order_id": "order_abc",
"value": 99.00,
"currency": "USD"
}
}
Supported event_name values: lead, sale, refund, cancellation, reversal.
Link Search and Retrieval API
GET /api/v1/links/find-Find link by URL or Domain/Slug
Look up a specific link based on its short URL or its domain ID and short code.
GET https://api.nimriz.com/api/v1/links/find?short_url=https://links.example.com/spring-launch
Authorization: Bearer <WORKSPACE_API_KEY>
Supported query parameters (must provide either short_url OR domain_id + short_code):
| Parameter | Description |
|---|---|
short_url | Full short URL to look up. |
domain_id | Domain UUID. |
short_code | Short code on the domain. |
GET /api/v1/links-List recent links
Returns a list of recently created links for the specified workspace.
GET https://api.nimriz.com/api/v1/links?account_id=WORKSPACE_UUID&limit=50
Authorization: Bearer <WORKSPACE_API_KEY>
Supported query parameters:
| Parameter | Description |
|---|---|
account_id | Workspace UUID. |
limit | Max number of links to return (max 100, default 50). |
GET /api/v1/links/:id-Get link by ID
Returns a single link record.
GET https://api.nimriz.com/api/v1/links/22222222-2222-2222-2222-222222222222
Authorization: Bearer <WORKSPACE_API_KEY>
QR API
Professional and Enterprise workspaces can automate link QR rendering, preset management, saved Static QR records, QR folders, QR-only link analytics, and Retail & GS1 workflows through the same customer API host.
QR API rules
- QR endpoints require the same workspace API key auth model shown above.
- Link QR rendering targets an existing Nimriz
url_id; link QR remains a view of a short link, not a separate QR object. - Saved Static QR endpoints manage fixed QR records in the QR Codes inventory.
- The API uses the same validation and plan checks as the dashboard, including QR CTA layout access and custom CTA text restrictions.
- The same canonical QR style state drives dashboard previews, downloads, preset saves, and API renders.
- Retail & GS1 endpoints require both QR API access and Retail & GS1 access. They are available on Professional and Enterprise workspaces.
Available QR endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/qr/generate | Render a QR asset for an existing link. |
GET | /api/v1/qr/presets | List QR presets visible to the workspace. |
POST | /api/v1/qr/presets | Create a QR preset. |
PUT | /api/v1/qr/presets/:id | Update a QR preset. |
DELETE | /api/v1/qr/presets/:id | Delete a QR preset. |
GET | /api/v1/qr/codes | List saved static QR codes. |
POST | /api/v1/qr/codes | Create a saved static QR code. |
PATCH | /api/v1/qr/codes/:id | Update a saved static QR code. |
DELETE | /api/v1/qr/codes/:id | Delete a saved static QR code. |
GET | /api/v1/qr/codes/:id/asset | Render a saved static QR code as PNG or SVG. |
GET | /api/v1/qr/folders | List QR folders with code counts. |
POST | /api/v1/qr/folders | Create a QR folder. |
GET | /api/v1/qr/retail/products | Load Retail & GS1 products, packaging variants, Digital Links, generated codes, resolver rules, and validation reports. |
POST | /api/v1/qr/retail/products | Create a Retail & GS1 product. |
POST | /api/v1/qr/retail/packaging | Create a packaging variant. |
POST | /api/v1/qr/retail/validate | Validate a GS1 Digital Link input without creating a record. |
POST | /api/v1/qr/retail/digital-links | Create a validated GS1 Digital Link record. |
POST | /api/v1/qr/retail/codes | Generate a GS1 QR or GS1 DataMatrix asset. |
GET | /api/v1/qr/retail/codes/:id/asset | Download a generated Retail & GS1 asset. |
POST | /api/v1/qr/retail/resolver-rules | Create a GS1 resolver rule. |
GET | /api/v1/links/:id/qr | Download the link's effective QR asset. |
GET | /api/v1/links/:id/analytics | Return link analytics with touch_type=qr_scan by default. |
The saved static QR endpoints manage records in your QR inventory. Static QR codes hold their content directly and are not tracked. Create a code with a name, a payload_type (url, text, phone, email, whatsapp, sms, location, wifi, vcard, mecard, or calendar), and a payload_json of the validated fields for that type. The same validation as the dashboard applies. There is no unauthenticated public generation API.
Retail & GS1 endpoints manage product-backed GS1 Digital Link records. Nimriz provides standards-aware validation for supported identifiers and generated assets, but it does not assign GTINs or certify retailer, POS, or supply-chain acceptance.
Batch ZIP export for generated Retail & GS1 assets is available through POST /api/exports/jobs with job_kind = "qr_bulk_download" and qr_source = "gs1_retail". See Exports for the request shape.
POST /api/v1/qr/generate-Render a QR asset
Renders a QR image from the requested link's saved QR state, with optional preset or style overrides.
POST https://api.nimriz.com/api/v1/qr/generate
Authorization: Bearer <WORKSPACE_API_KEY>
Content-Type: application/json
{
"url_id": "22222222-2222-2222-2222-222222222222",
"format": "png",
"size": 1024,
"force_regenerate": false,
"qr_preset_id": "55555555-5555-5555-5555-555555555555",
"style": {
"background": { "mode": "transparent" }
}
}
Notes:
url_idis required and must belong to a link the API key can access.formatcan bepngorsvg. Defaults tosvgwhen omitted.sizeis clamped to128-2048. The default is512.force_regenerate: truebypasses any cached asset and rebuilds the QR output.qr_preset_idis optional.styleis optional and must match the same QR style schema the dashboard uses.
Successful responses return the QR asset itself with the correct image content type and download headers.
GET /api/v1/qr/presets-List QR presets
Lists QR presets for the authenticated workspace.
GET https://api.nimriz.com/api/v1/qr/presets
Authorization: Bearer <WORKSPACE_API_KEY>
Successful responses return:
{
"ok": true,
"presets": [
{
"id": "55555555-5555-5555-5555-555555555555",
"name": "Conference handout",
"style": {},
"is_default": true,
"created_at": "2026-04-12T10:00:00.000Z",
"updated_at": "2026-04-12T10:00:00.000Z",
"logo_preview_url": "https://..."
}
]
}
POST /api/v1/qr/presets-Create a QR preset
Creates a reusable QR preset using the same style contract as the dashboard.
{
"name": "Conference handout",
"is_default": true,
"style": {
"background": { "mode": "transparent" }
}
}
Rules:
- Preset names are limited to 120 characters.
- Plan preset limits still apply.
- Frame entitlement checks still apply.
- If
is_defaultistrue, Nimriz clears the previous default preset in that workspace.
PUT /api/v1/qr/presets/:id-Update a QR preset
Updates one or more preset fields.
{
"name": "Conference handout v2",
"is_default": false,
"style": {
"background": { "mode": "single", "color": "#ffffff" }
}
}
You can send any subset of name, is_default, and style. The same style validation and frame plan checks apply as on create.
DELETE /api/v1/qr/presets/:id-Delete a QR preset
Deletes the target preset and returns the deleted preset id on success.
Retail & GS1 API
Retail & GS1 API calls use the same API key header as the rest of the customer API. They require a Professional or Enterprise workspace with QR API access and Retail & GS1 access.
Create a product:
POST https://api.nimriz.com/api/v1/qr/retail/products
Authorization: Bearer <WORKSPACE_API_KEY>
Content-Type: application/json
{
"brand": "Acme",
"name": "Trail Mix",
"gtin": "09506000134352",
"target_market": "US"
}
Validate a Digital Link input:
{
"gtin": "09506000134352",
"resolver_host": "id.example.com",
"resolver_path_prefix": "01",
"batch_lot": "LOT42",
"expiry": "270630"
}
Successful validation responses include ok, validation errors, warnings, normalizedIdentifiers, canonicalUri, and elementString.
Generate a code:
{
"digital_link_id": "55555555-5555-5555-5555-555555555555",
"symbology": "gs1_datamatrix",
"format": "svg",
"size": 512,
"quiet_zone": 4
}
symbology can be gs1_qr or gs1_datamatrix. format can be svg or png.
GET /api/v1/links/:id/qr-Download the effective QR asset
Fetches the saved QR asset for an existing link.
GET https://api.nimriz.com/api/v1/links/22222222-2222-2222-2222-222222222222/qr?format=svg&size=1024&force_regenerate=1
Authorization: Bearer <WORKSPACE_API_KEY>
Supported query parameters:
| Parameter | Description |
|---|---|
format | png or svg. Defaults to svg. |
size | Optional size from 128-2048. Defaults to 512. |
force_regenerate | 1, true, yes, or on forces a fresh render. |
GET /api/v1/links/:id/analytics-Fetch QR-filtered analytics
Returns per-day totals and points for the target link. If you do not pass touch_type, the endpoint defaults to qr_scan.
GET https://api.nimriz.com/api/v1/links/22222222-2222-2222-2222-222222222222/analytics?range_days=30&include_bots=0&touch_type=qr_scan
Authorization: Bearer <WORKSPACE_API_KEY>
Supported query parameters:
| Parameter | Description |
|---|---|
range_days | Optional daily range from 1-365. Defaults to 30. |
include_bots | 0/1 or boolean-like values. Defaults to 0. |
touch_type | qr_scan or short_link_click. Defaults to qr_scan. |
Successful responses return:
{
"ok": true,
"url_id": "22222222-2222-2222-2222-222222222222",
"range_days": 30,
"include_bots": false,
"touch_type": "qr_scan",
"available": true,
"totals": {
"clicks": 120,
"bot_clicks": 8,
"human_clicks": 112,
"selected_clicks": 112
},
"points": [
{
"date": "2026-04-01",
"clicks": 12,
"bot_clicks": 1,
"human_clicks": 11,
"selected_clicks": 11
}
]
}
Common error codes
| Code | HTTP | Meaning |
|---|---|---|
plan_limit | 403 | The request exceeds a per-plan object cap (for example QR presets, saved static QR codes, or QR folders). |
monthly_quota_exceeded | 429 | Monthly link-creation quota exceeded. |
slug_reserved | 403 | Slug is below the domain's minimum custom-slug length. A reserved system path on a Nimriz domain returns 400. |
slug_tombstoned | 409 | Slug was recently deleted and is still held. A slug that is simply taken returns 409 with "Slug already exists" and no code field. |
domain_disabled | 403 | Target domain is inactive. |
account_suspended | 403 | Account is suspended. |
custom_domain_not_ready | 403 | Domain is not yet Ready for traffic. The response includes reason_code and per-record checks. |
destination_blocked | 400 | Destination host is on the blocklist, or outside the allowlist when one is configured. Forbidden schemes and Nimriz-managed hosts are rejected with 400 and a message but no code field. |
invalid_expires_at | 400 | Expiration timestamp is malformed. A timestamp in the past returns expires_in_past (400). |
expiration_required | 403 | Domain policy requires an expiration date. |
expiration_update_not_allowed | 403 | Expiration updates are not available on this plan or domain. |
password_not_allowed | 403 | Password-protected links are not available on your plan. |
feature_not_enabled | 403 | The requested feature area (e.g., QR API, destinations, webhooks, conversion tracking) is not available on your plan. |
Retry guidance
- Do not retry
4xxerrors immediately-they indicate input problems or policy failures that will not resolve with a retry. - Do retry
429,502,503,504with exponential backoff. - For bulk import jobs, use
POST /api/shorten/bulkwith stableidempotency_keyvalues so retries are safe.
Related guides
Related next steps
Ready to test this setup?
Create an account to try the workflow, or compare plans when the setup needs higher limits, integrations, or team controls.