Skip to main content

Tokens

Looking for the product view?
The Help Center explains this surface as users see it: External links.

Apps use the app_view token type in two distinct ways, and it pays to keep them apart:

KindCreated byStored?Purpose
Runtime view tokensThe platform, automatically, when it serves an app's host pageNo. Stateless JWTs; all claims (appId, username, roles) travel in the token itselfAuthorize the app's own API traffic (proxy calls, handler dispatch, assets) for the duration of a view
External-link tokensA person, via the External Links dialog or this APIYes. A Token row per linkShare an app with people who have no Informer account

GET /api/apps/{id}/tokens lists only the second kind. Runtime tokens never appear in any list because they are never written to the database.

GET /api/apps/{id}/tokens​

Get the external-link token rows associated with an app.

Authentication: Required

Permissions Required: None (any user who can read the app)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
List an App's tokensGET /api/apps/admin%3Aorder-desk/tokens
Every row here is user-created (External Links or API tokens UI). Runtime app_view tokens are stateless JWTs and never appear in this list.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/tokens"
}
},
"_embedded": {
"inf:app-token": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tokens/00000000-0000-4000-8000-000000000001"
},
"inf:token-type": {
"href": "https://informer.example.com/api/token-types/app_view"
}
},
"permissions": {
"edit": true,
"delete": true
},
"id": "00000000-0000-4000-8000-000000000001",
"type": "app_view",
"readOnly": true,
"notes": "Quarterly ops review link",
"restrict": null,
"host": null,
"cidr": null,
"data": {
"roles": []
},
"requests": 0,
"blocked": 0,
"lastAccessedAt": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"username": "admin",
"reportId": null,
"queryId": null,
"visualId": null,
"datasourceId": null,
"datasetId": null,
"appId": "00000000-0000-4000-8000-000000000002",
"assistantId": null,
"oauthClientId": null,
"typeName": "App View Token",
"key": "eyJhbGciOiJIUzI1NiJ9.example-token-1.signature"
}
]
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

Rows are embedded under _embedded["inf:app-token"]. Each row's key is the shareable token string, a JWT signed at response time; it is never stored in the database (this prevents session stealing by reporting on Informer's own tables).

Error Responses:

  • 404 Not Found - App doesn't exist or user lacks access

External-link tokens are created through the general Token API (POST /api/tokens), not through an app-specific endpoint. This is exactly the request the External Links dialog sends:

Create an external-link tokenPOST /api/tokens
This is the payload the External Links dialog sends. appId must be the app UUID, not the natural id. The key is a signed JWT computed at response time, never stored.
Request body
{
"type": "app_view",
"appId": "00000000-0000-4000-8000-000000000001",
"readOnly": true,
"notes": "Quarterly ops review link",
"data": {
"roles": []
}
}
Response · 201
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tokens/00000000-0000-4000-8000-000000000002"
},
"inf:token-type": {
"href": "https://informer.example.com/api/token-types/app_view"
}
},
"resource": "Order Desk",
"permissions": {
"edit": true,
"delete": true
},
"id": "00000000-0000-4000-8000-000000000002",
"type": "app_view",
"readOnly": true,
"notes": "Quarterly ops review link",
"restrict": null,
"host": null,
"cidr": null,
"data": {
"roles": []
},
"requests": 0,
"blocked": 0,
"lastAccessedAt": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"username": "admin",
"reportId": null,
"queryId": null,
"visualId": null,
"datasourceId": null,
"datasetId": null,
"appId": "00000000-0000-4000-8000-000000000001",
"assistantId": null,
"oauthClientId": null,
"user": {
"permissions": {
"changeDomain": true,
"changeProfile": true,
"changePassword": false,
"manageMfa": true,
"delete": true,
"edit": true,
"reassign": true,
"superuser": true,
"enable": true,
"changeTheme": true,
"lock": true,
"setGlobalPermissions": true,
"manageLogin": false
},
"displayName": "acme Administrator",
"email": null
},
"assistant": null,
"oauthClient": null,
"typeName": "App View Token",
"key": "eyJhbGciOiJIUzI1NiJ9.example-token-1.signature",
"_embedded": {
"inf:app": {
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001"
},
"inf:owner": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/owner"
},
"inf:draft": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/draft"
},
"inf:app-shares": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/shares"
},
"inf:app-tags": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/tags"
},
"inf:favorite": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/favorite"
},
"inf:app-tag": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/tags/{tagId}",
"templated": true
},
"inf:export-bundle": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/_export"
},
"inf:cloud-publish": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/_cloud-publish"
},
"inf:copy": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/_copy"
},
"inf:print": [
{
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/_print"
},
{
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/_print",
"title": "Print to PDF"
}
],
"inf:migrate": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/_migrate"
},
"inf:deploy": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/_deploy"
},
"inf:app-mcp": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/mcp"
},
"inf:app-tokens": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/tokens"
},
"inf:app-roles": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/roles"
},
"inf:app-dependencies": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/dependencies"
},
"inf:app-dependency": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/dependencies/{name}",
"templated": true
},
"inf:app-environment": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/environment"
},
"inf:app-environment-var": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/environment/{key}",
"templated": true
},
"inf:app-suggest-icon": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/_suggest-icon"
},
"inf:app-modernize-manifest": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/_modernize-manifest"
},
"inf:app-draft-diff": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/draft/_diff"
},
"edit": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/_edit"
},
"inf:app-share": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/shares/{principalId}",
"templated": true
},
"inf:entrypoint": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/view/",
"title": "index.html"
},
"inf:library": {
"href": "https://informer.example.com/api/libraries/00000000-0000-4000-8000-000000000003",
"title": "App Files"
},
"inf:library-contents": {
"href": "https://informer.example.com/api/libraries/00000000-0000-4000-8000-000000000003/contents/index.html",
"title": "Raw Contents"
},
"share": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/view/?token=%7Btoken%7D",
"title": "Share Page"
}
},
"naturalId": "admin:order-desk",
"isManaged": false,
"permissions": {
"assignTags": true,
"bind": true,
"changeOwner": true,
"copy": true,
"delete": true,
"edit": true,
"manage": true,
"purge": true,
"restore": true,
"run": true,
"share": true,
"switchRoles": true,
"unmanage": true,
"write": true
},
"id": "00000000-0000-4000-8000-000000000001",
"type": "report",
"name": "Order Desk",
"tenant": "acme",
"ownerId": "admin",
"slug": "order-desk",
"description": null,
"defn": {
"entryPoint": "index.html"
},
"settings": {
"sandbox": true,
"passDatasets": true
},
"origin": "ui",
"source": null,
"sourceId": null,
"shared": false,
"defnUpdatedAt": null,
"libraryId": "00000000-0000-4000-8000-000000000003",
"folderId": null,
"deletedAt": null,
"editingId": null,
"deployedAt": null,
"editingSession": null,
"editingHeartbeatAt": null,
"editingLockedBy": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"contentModifiedAt": "2026-01-15T16:20:00.000Z"
}
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

Payload Fields:

FieldTypeDescription
typestringMust be "app_view" (underscore, not a dash)
appIdstringApp UUID. Natural ids are rejected (appId is a UUID foreign key); resolve the app first if you only have ownerId:slug.
readOnlybooleanThe External Links dialog always sends true, restricting the link to GETs and routes explicitly marked read-only safe
notesstringFree text for tracking where the link is used
restrictstringOptional network restriction: host or cidr
hoststringHostname or IP, when restrict: "host"
cidrstringIP range (e.g. 192.168.1.0/24), when restrict: "cidr"
data.rolesarrayApp role ids (from the manifest's roles:) granted to visitors using this link

Permissions Required: tokens:create

The response is 201 Created. The key in the response is the token string to embed in the share URL.

The share URL​

https://<your-informer>/api/apps/{id}/view?token=<key>

The token travels as a ?token= query parameter on the view URL; visitors need no Informer account and see only the app.

Editing and revoking​

  • PUT /api/tokens/{tokenId} updates notes, network restrictions, and roles (the External Links dialog's edit mode).
  • DELETE /api/tokens/{tokenId} revokes the link immediately.

What an app_view token can reach​

Authorization is a closed allowlist of route ids, checked on every request. A token (runtime or external-link) authorizes only the app-render surfaces:

  • GET /api/apps/{id}/view/{path*} - the app's host page and SPA routes
  • GET /api/apps/{id}/launch/{path*} - the standalone/home-screen entry
  • GET /api/apps/{id}/launch/icon-180.png - the iOS tile icon
  • /api/apps/{id}/view/api/{path*} - the data-access proxy (all methods, subject to the app's declared API whitelist)
  • /api/apps/{id}/view/_/{path*} - the app's own server-route handlers
  • GET /api/apps/{id}/view/-/{path*} - static assets

When Apps are served from their own origins, the token (carried by the App origin's session cookie) also reaches that origin's own routes. Each resolves the App from the origin, and /_socket refuses (403) a token issued for a different App:

  • GET /_launch - session establishment
  • GET /_socket - the App Channels credential mint (when channels are enabled)

The home-screen manifest and tile icons (/_app/manifest.webmanifest, /_app/icon-{size}.png) need no token: they are anonymous, so an installed App's tile keeps updating after its session expires.

Everything else returns 401, even with a valid token for that app: the app detail GET, _export, _copy, contents, and every other app-scoped route are outside the allowlist. The token must also match the app in the URL (appId or natural id), so a token for one app can never be replayed against another.

Read-only enforcement: when readOnly: true (every external link), the session additionally rejects any request that is not a GET or not explicitly marked read-only safe. Runtime view tokens are minted with readOnly: false because apps legitimately POST through the proxy; their gating comes from the route allowlist and the app's API whitelist.


Security Considerations​

  • Treat the key as a password. Anyone holding the URL has the link's access until the row is deleted.
  • Tokens are visible to anyone who can read the app via the list route, including each link's key.
  • Scope with roles. Grant link visitors only the app roles they need; an empty data.roles array grants none.
  • Pin the network when you can. restrict: "cidr" keeps a link from working outside your office or VPN range.
  • The data whitelist still applies. Proxy calls through /view/api/ are validated against the app's declared API access, regardless of who is asking.
  • GET /api/apps/{id}/webhook-tokens - revocable tokens for inbound webhooks (a separate token type)
  • GET /api/tokens / GET /api/tokens/{id} - the general Token API
  • GET /api/apps/{id}/data-access - the whitelist that bounds proxy calls