Tokens
Apps use the app_view token type in two distinct ways, and it pays to keep
them apart:
| Kind | Created by | Stored? | Purpose |
|---|---|---|---|
| Runtime view tokens | The platform, automatically, when it serves an app's host page | No. Stateless JWTs; all claims (appId, username, roles) travel in the token itself | Authorize the app's own API traffic (proxy calls, handler dispatch, assets) for the duration of a view |
| External-link tokens | A person, via the External Links dialog or this API | Yes. A Token row per link | Share 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:
| Parameter | Type | Description |
|---|---|---|
id | string | App UUID or natural ID |
{
"_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"
}
]
}
}
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
Creating an external link
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:
{
"type": "app_view",
"appId": "00000000-0000-4000-8000-000000000001",
"readOnly": true,
"notes": "Quarterly ops review link",
"data": {
"roles": []
}
}
{
"_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"
}
}
}
Payload Fields:
| Field | Type | Description |
|---|---|---|
type | string | Must be "app_view" (underscore, not a dash) |
appId | string | App UUID. Natural ids are rejected (appId is a UUID foreign key); resolve the app first if you only have ownerId:slug. |
readOnly | boolean | The External Links dialog always sends true, restricting the link to GETs and routes explicitly marked read-only safe |
notes | string | Free text for tracking where the link is used |
restrict | string | Optional network restriction: host or cidr |
host | string | Hostname or IP, when restrict: "host" |
cidr | string | IP range (e.g. 192.168.1.0/24), when restrict: "cidr" |
data.roles | array | App 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 routesGET /api/apps/{id}/launch/{path*}- the standalone/home-screen entryGET /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 handlersGET /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 establishmentGET /_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.rolesarray 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.
Related Endpoints
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 APIGET /api/apps/{id}/data-access- the whitelist that bounds proxy calls