Personal Access Tokens (Beta)
Beta A Personal Access Token authenticates API requests as you, without a browser session. Tokens are being released to a limited group of accounts; if you do not see the section described below, it is not yet enabled for yours.
What a token can do
A token carries scopes, and reaches only what its scopes cover:
| Scope | Grants |
|---|---|
files:upload | Uploading files, including in parts, and importing them from a URL |
files:read | Listing the files in a folder, and reading one with its transcript and summary |
files:ask | Asking questions about a file and analysing it with AI |
Every scope is deliberately narrow: files:upload cannot read and files:read
cannot write. files:ask is separate from files:read because asking spends your
account’s AI credits, so reading a file back never silently costs you any. A token
cannot change your account, manage your team, touch your subscription, or create
further tokens, whatever your own account is permitted to do.
A token reaches only an explicit list of methods and paths. Everything on this page
is on it, along with the remaining multipart calls — confirming a part, aborting an
upload, and checking its status. Anything else answers 401, even a request your
account can make in the app: reading a file is permitted while writing to that same
path is not.
Creating a token
- Open screenapp.io/app/#/settings/api.
- Find Personal Access Tokens and select Add new token.
- Give it a name — the application that will use it is a good one — and optionally a description.
- Choose a lifetime. The default is 90 days; No expiration creates a token that works until you revoke it.
- Select the scopes it needs —
files:uploadto send files,files:readto read them and their transcripts back,files:askto analyse them with AI. - Select Create Token, then copy it. It is shown once and never again.
Tokens look like sk1- followed by 40 hexadecimal characters. Only the first 12
characters are stored in a readable form, so we can show you which token is which
but cannot recover one you have lost. If you lose a token, rotate it.
Authenticating
Send the token in either header. Do not use Authorization: Bearer — that is for
session tokens, and a token sent that way is ignored.
x-screenapp-token: sk1-your-token-hereAuthorization: ScreenApp-Token sk1-your-token-hereYou need your team ID and the ID of the destination folder. Both appear in the URL when you open that folder in the app.
Uploading a file
For files up to 100 MB, ask for an upload URL, PUT the file to it, then finalize.
1. Request an upload URL
POST /v2/files/upload/urls/{teamId}/{folderId}Content-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "files": [{ "name": "my-video.mp4", "contentType": "video/mp4" }]}{ "success": true, "data": { "uploadParams": [ { "fileId": "file_123", "uploadUrl": "https://s3.amazonaws.com/presigned-url" } ] }}2. Send the bytes
PUT https://s3.amazonaws.com/presigned-urlContent-Type: video/mp4
[binary file data]The presigned URL carries its own authorization. Do not send your token to it.
3. Finalize
Until you finalize, the file does not appear in your library and is not processed.
POST /v2/files/upload/finalize/{teamId}/{folderId}Content-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "file": { "fileId": "file_123", "contentType": "video/mp4", "name": "My Video Recording", "description": "Optional description" }}Uploading a large file in parts
For anything larger, upload in parts. Parts may be sent in parallel, and each must be at least 5 MB except the last.
1. Initialize
PUT /v2/files/upload/multipart/init/{teamId}/{folderId}Content-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "contentType": "video/mp4" }{ "success": true, "data": { "fileId": "file_123", "uploadId": "upload_456" } }2. Get a URL per part
Part numbers start at 1.
PUT /v2/files/upload/multipart/url/{teamId}/{folderId}/{fileId}/{uploadId}/{partNumber}Content-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "contentType": "video/mp4" }{ "success": true, "data": { "uploadUrl": "https://s3.amazonaws.com/presigned-url-part-1" } }3. Send each part, then finalize
PUT /v2/files/upload/multipart/finalize/{teamId}/{folderId}/{fileId}/{uploadId}Content-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "file": { "contentType": "video/mp4", "name": "Large Video File", "notes": [] }}Finalizing compares the parts it recorded issuing against what actually arrived in
storage. If any are missing you get 409, and re-uploading them before finalizing
again is the fix, rather than publishing a truncated file. Treat this as a safety
net and not a guarantee: keep your own list of the parts you sent and check it
before you finalize.
Abandoning or checking an upload
Abort releases the parts already sent, so a run you have given up on does not sit around:
POST /v2/files/upload/multipart/abort/{teamId}/{folderId}/{fileId}/{uploadId}x-screenapp-token: sk1-your-token-hereStatus reports what storage has received so far, which is useful when resuming:
GET /v2/files/upload/multipart/status/{fileId}x-screenapp-token: sk1-your-token-hereThere is also a confirm call per part, which reports a part as landed and helps
us spot uploads that lose one. It is optional and its absence never fails an
upload.
Importing from a URL
POST /v2/files/import/{teamId}/{folderId}Content-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "options": { "intent": "transcribe" }}YouTube, Vimeo, Twitter/X and direct media URLs are supported. Importing is asynchronous: the response identifies the file, and transcription continues after it returns.
Reading files and transcripts
Requires files:read.
List the files in a folder. Paginated with page and limit, defaulting to page 1
and 10 per page. Folders are not returned, so take the folder id from the app URL:
GET /v2/files?page=1&limit=50x-screenapp-token: sk1-your-token-hereRead one file. The transcript and summary come back on this response, so there is no separate transcript call:
GET /v2/files/{fileId}x-screenapp-token: sk1-your-token-hereTranscription runs after an upload or import finishes, so poll this until the transcript is present rather than expecting it in the finalize response.
Asking a question about a file
Requires files:ask.
Ask anything about a file that has finished processing, and get an answer back on the same response. This is the endpoint behind Ask AI in the app, so it draws on the file’s transcript and, when you ask for it, the video itself.
POST /v2/files/{fileId}/ask/multimodalContent-Type: application/jsonx-screenapp-token: sk1-your-token-here
{ "promptText": "Summarise the customer's objections and the commitments we made."}{ "success": true, "data": { "answer": { "role": "assistant", "content": "The customer raised three objections…", "partial": false, "refusal": "" }, "sessionId": "session_123" }}Answers accumulate into a conversation per file, identified by sessionId, so
follow-up questions build on the ones before them.
To analyse the picture rather than only the words, pass mediaAnalysisOptions.
Video analysis is metered separately from text questions and costs more of your
allowance:
{ "promptText": "What is shown on screen when pricing is discussed?", "mediaAnalysisOptions": { "video": { "segments": [{ "start": 0, "end": 600 }] } }}Every question spends your account’s AI allowance. Once it runs out the endpoint
answers 403 until the allowance resets, so ask against a finished transcript
rather than polling with questions.
Wait for transcription to finish before asking. Asking too early answers against an incomplete transcript rather than failing.
Webhook notifications
Webhooks need no token and no scope. Configure the destination URL in your settings in the app, and ScreenApp posts to it as work completes. A token cannot read or change webhook settings.
Errors
| Status | Meaning |
|---|---|
401 with code: INVALID_PERSONAL_ACCESS_TOKEN | The token is unknown, expired, revoked, not enabled for the account, or used on an endpoint not listed on this page. Check you copied it whole and that it has not expired. |
403 | The token is valid but not permitted. Usually a scope it lacks, though it can also be the underlying file or team permission, exactly as it would be in the app, or an exhausted AI allowance when asking a question. |
404 on a token management route | Personal Access Tokens are not enabled for this account. |
409 on multipart finalize | Parts are missing. Re-upload them and finalize again. |
A 401 always concerns the token itself — unknown, expired, revoked, disabled, or
used somewhere tokens may not go. A 403 means authenticated but not allowed, so
check the token’s scopes first and then whether the account itself may do it.
Managing tokens
From the same settings page you can:
- Edit a token — change its name, description or scopes without changing its value. Whatever already uses the token keeps working, so this is how to widen or narrow an integration’s access without a redeploy. A token’s lifetime cannot be changed; rotate or replace it instead.
- Rotate a token — replaces its value while keeping its name, scopes and lifetime. The old value stops working immediately, so update whatever uses it first.
- Revoke a token — stops it permanently.
The list shows when each token was last used, which is the quickest way to find one nothing is using any more.
Keeping tokens safe
- Treat a token like a password. Keep it out of version control and out of logs.
- Give a token only the scopes its job needs.
- Prefer an expiring token. Reach for No expiration only where nothing can rotate it.
- Rotate on a schedule, and immediately if a token may have leaked.
- Revoke tokens you have stopped using — the last-used column shows which.
Need help? See the API overview or write to support@screenapp.io.