Import links#
ThinkWatch Lite accepts links that open the New upstream dialog with the settings already filled in. A relay, a model vendor or a ThinkWatch Enterprise console can place such a link in its dashboard or in a welcome message. The user opens it, checks the settings in the same dialog used to add an upstream by hand, and creates the upstream with a few clicks.
This page is written for relay and vendor operators. It describes the two forms of the link, the parameters grouped the way the dialog groups them, which service takes which parameter, the encodings, and what a link intentionally cannot set. A link builder is at the end of the page. ThinkWatch Lite can also write such a link for an existing upstream; see Generated by Lite.
The parameters other than name, url, protocol, key and models need ThinkWatch Lite 2026.10.16 or later, and limits needs 2026.10.26 or later. A link that uses only those five also opens in earlier versions, and every link written for earlier versions stays valid.
Link forms#
Both forms carry the same parameters.
| Form | Description |
|---|---|
thinkwatch://import?… | Opens the app directly. The browser asks for permission before it hands the link to the app. thinkwatch://import/?… is accepted too. |
https://thinkwat.ch/import#… | A web page that shows the hosts that will receive requests and the settings, opens the app with a button, and offers the download when the app is not installed. The parameters are in the fragment after #, which browsers do not send to any server. The page loads no analytics or third-party scripts and removes the fragment from the address bar after reading it. |
The web form suits emails and dashboards whose users may not have the app yet. The page checks the parameters with the same rules as the app and shows nothing for a link that fails them.
Parameters#
A parameter is accepted only where the New upstream dialog shows its field for the chosen service and authentication method. A value the dialog fills in by itself, such as the address of an official service or the only protocol it speaks, may be repeated in the link but not changed. By service lists what each service takes.
Service#
| Parameter | Value |
|---|---|
service | anthropic, openai, gemini, deepseek, zai, bedrock, openrouter, thinkwatch (a ThinkWatch Enterprise gateway), sub2api, newapi (New API / One API), ollama or custom. When omitted, the app recognizes the service from url the way the edit dialog does: official addresses by host, standard Bedrock addresses, port 11434 as Ollama, and any other address as custom. When a parameter of the link does not fit the recognized service, the link is a custom upstream, which takes every parameter. An explicit service is never changed. |
auth | key (the default), oauth, account or aws-keys, among the methods the service offers. account is the ChatGPT account for openai and the account sign-in for zai. |
site | bigmodel (mainland China) or zai (international), for zai only. When omitted, it is taken from url, or is bigmodel. |
Connection#
| Parameter | Value |
|---|---|
name | The upstream’s name in the app. When omitted, the app chooses one from the service or the address. |
url | The base URL, without endpoint paths such as /chat/completions or /messages. https://, or http:// on this computer or a private network (see below). No user name or password, query string or fragment. Required for custom, thinkwatch, sub2api and newapi. Optional for Bedrock (an endpoint such as a VPC endpoint) and Ollama (another machine). Services with their own address accept only that address. Not accepted with auth=account. |
protocol | anthropic, openai-chat, openai-responses or gemini, where the dialog offers a choice. When omitted, the app detects it from the address. |
region | The AWS region, such as eu-west-1, for Bedrock only. Without url, it chooses the standard address of that region. With a standard address, it must match the region in it. With another address, it is the signing region and needs auth=aws-keys. |
key | The API key, stored as given (for Bedrock, the Bedrock API key). With auth=key. Ollama has no key field. |
oauth_endpoint | The token endpoint, with the same rules as url and usually a path. Required with auth=oauth. When its host differs from the address, the app names both hosts. |
oauth_refresh | The refresh token. Required with auth=oauth. |
oauth_client_id | The client ID, with auth=oauth. Optional. |
oauth_client_secret | The client secret, with auth=oauth. Optional. |
aws_access_key_id | The access key ID, with service=bedrock and auth=aws-keys. Required there. |
aws_secret_access_key | The secret access key. Required with auth=aws-keys. |
aws_session_token | The session token of temporary credentials, with auth=aws-keys. Optional. |
Plain http:// is accepted in url and oauth_endpoint for addresses on this computer or a private network, such as an enterprise gateway on the company intranet. The host must be localhost, 127.0.0.1 or [::1]; an IPv4 address in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 or 169.254.0.0/16; an IPv6 address in fc00::/7 or fe80::/10; a name without a dot; or a name ending in .local, .lan, .internal, .intranet, .corp, .home.arpa or .localhost. Any other http:// address is rejected. For a plain http:// address other than this computer, the dialog and the web page state under the host that the key and request content travel unencrypted.
Advanced settings#
| Parameter | Value |
|---|---|
headers | Extra request headers, one Name: Value per line. The encoding is described under Encodings. Not accepted with auth=account. |
client_identity | on turns on Forward client identity, for relays that accept only specific clients. Sub2API, New API and custom upstreams only. |
concurrency | The concurrency limit: a whole number from 1 to 1000, without a sign or leading zeros. Not accepted with auth=account. |
limits | Usage limits, entries separated by ;, each per|measure|max|every|cache_reads, such as 10,000,000 tokens every 5 minutes or $20 a day. The encoding is described under Encodings. Not accepted with auth=account. |
balance | Where the balance is read: off, openrouter, deepseek, moonshot, sub2api, newapi or thinkwatch. When omitted, the source is detected automatically. Custom upstreams only; other services decide their own source. |
Models#
| Parameter | Value |
|---|---|
models | Comma-separated model IDs, added to the upstream by hand. The app offers them next to the models the service lists, so a service that lists only some of its models can name the rest here; when the service lists none, they are its whole list. Repeated IDs are kept once. |
models_only | Comma-separated model IDs or patterns with * and ?. Sets the enabled models to these models only. |
specs | Model specs, entries separated by ;, each model|context|max_output|reasoning|image. They are saved once the upstream exists, for models on its final list. A spec for a model that is neither in models nor listed by the upstream is not saved, and the Models step says so. |
Billing#
| Parameter | Value |
|---|---|
billing | per-token or free. When omitted, the service’s default applies: free for Ollama, per token for the others. |
price_multiplier | The multiplier of a new price sheet, above 0 and at most 100, such as 0.8. Not accepted with free billing. |
prices | Price overrides for the new price sheet, entries separated by ;, each model|input|output|cache_read|cache_write_5m|cache_write_1h. Not accepted with free billing. |
When price_multiplier or prices is present, the Billing step preselects a new price sheet named after the upstream, with the multiplier (1 when omitted) and the overrides from the link. The sheet can be edited like any new sheet and is created together with the upstream when Create is chosen. When a sheet of that name exists, the dialog asks for another name, as it does for a taken upstream name.
By service#
| Service | service | auth | Address | Protocol |
|---|---|---|---|---|
| Anthropic | anthropic | key | https://api.anthropic.com | anthropic |
| OpenAI | openai | key, account (ChatGPT account) | https://api.openai.com | openai-chat (default) or openai-responses |
| Google Gemini | gemini | key | https://generativelanguage.googleapis.com | gemini |
| DeepSeek | deepseek | key | https://api.deepseek.com/anthropic | anthropic |
| Z.ai / BigModel | zai | key, account | https://open.bigmodel.cn/api/anthropic or https://api.z.ai/api/anthropic, by site | anthropic |
| Amazon Bedrock | bedrock | key (Bedrock API key), aws-keys | The standard address of region, or an endpoint in url | Not set by a link |
| OpenRouter | openrouter | key | https://openrouter.ai/api | openai-chat |
| ThinkWatch Enterprise | thinkwatch | key | url, required | Not set by a link; the gateway accepts each client’s format |
| Sub2API | sub2api | key | url, required | Any, or detected |
| New API / One API | newapi | key | url, required | Any, or detected |
| Ollama | ollama | key, without a key field | http://127.0.0.1:11434, or another address in url | openai-chat |
| Custom | custom | key, oauth | url, required | Any, or detected |
headers, concurrency and limits apply to every service except an account sign-in. client_identity applies to Sub2API, New API and custom upstreams, and balance to custom upstreams. The parameters of the Models and Billing groups apply to every service, account sign-ins included.
A link with auth=account carries no credential: the sign-in happens in the app. Such a link takes service, auth, site, name and the Models and Billing parameters only.
Encodings#
- Each value is percent-encoded, for example with
encodeURIComponent, and decoded once. A literal+means a space, so a key that contains+is written as%2B. - Each parameter appears at most once, and an empty value is rejected.
- No value may contain
$,{or}, control characters or invisible characters. The app reads${NAME}from the user’s environment, so these characters could make it send a local secret to the link’s host. - A value that fails its rule rejects the whole link. The app records only the reason, never the content.
Lists and entries:
| Parameter | Encoding | Example value, before percent-encoding |
|---|---|---|
models | Model IDs separated by ,; letters, digits and - _ . : / @ + | glm-5,glm-5-air |
models_only | As models, plus * and ? in patterns | claude-*,gpt-5 |
headers | Lines separated by a line feed (%0A), each Name: Value | X-Relay-Group: pro, line feed, X-Relay-Region: hk |
specs | Entries separated by ;, each model|context|max_output|reasoning|image | glm-5-air|128000|96000|y|n |
prices | Entries separated by ;, each model|input|output|cache_read|cache_write_5m|cache_write_1h | glm-5|1|3.2|0.2 |
limits | Entries separated by ;, each per|measure|max|every|cache_reads | minute|tokens|10000000|5;day|cost|20 |
In headers, a name uses the token characters of HTTP (letters, digits and ! # % & ' * + - . ^ _ ` | ~), and a value is printable ASCII with surrounding spaces removed. No name may appear twice, ignoring case. Headers that carry credentials or that the gateway writes itself are rejected: Authorization, Proxy-Authorization, X-Api-Key, X-Goog-Api-Key, Api-Key, Cookie, Host, Content-Length, Content-Type, Content-Encoding, Transfer-Encoding, Connection, Keep-Alive, Upgrade, TE, Trailer, User-Agent, and any name starting with Proxy- or Sec-.
In specs, context and max_output are token counts from 1 to 10,000,000, and reasoning and image are y or n. Any of the four may be empty, but not all of them.
In prices, each price is in USD per million tokens, with up to six digits before and six after the decimal point. input and output are required. A cache price that is empty or left out takes the input price.
In limits, per is minute, hour, day, week or month, and measure is requests, tokens or cost. For requests and tokens, max is a whole number from 1 to 999,999,999,999,999 without leading zeros; for cost, it is an amount in US dollars of at least 0.01, with up to nine digits before the decimal point and six after and no leading zeros, such as 5 or 0.5. every is how many minutes (1 to 60) or hours (1 to 24) the window spans, for minute and hour only, and is 1 when empty. cache_reads is y to count cache reads, for tokens only, and is off when empty. Empty fields at the end may be left out, so day|cost|5 is a complete entry. No two entries may have the same per, every, measure and cache_reads.
Limits:
| Item | Limit |
|---|---|
| Whole link | 8192 characters |
name | 64 characters; no leading or trailing spaces; none of /, \, $, {, }, <, >, " or the backtick; not . or ..; not starting with __ |
url, oauth_endpoint | 2048 characters |
key, oauth_client_id, oauth_client_secret, aws_secret_access_key | 512 characters; letters, digits and - _ . ~ + / = : |
oauth_refresh, aws_session_token | 4096 characters; the same characters as key |
aws_access_key_id | 16 to 128 capital letters and digits |
region | 32 characters; lowercase letters, digits and - |
headers | 16 lines; names up to 64 characters, values up to 1024 |
models, models_only, specs, prices | 64 entries each; model IDs up to 128 characters; no model twice in specs or prices |
limits | 16 entries |
What the app does with a link#
- Checks the link. Every rule on this page is applied in the app, whatever the page that produced the link has checked.
- Opens the New upstream dialog, filled in. The dialog opens on its second step, Connection, or Account for an account sign-in. A note under the steps names the host that will receive requests and keys, and the token endpoint’s host when it differs. Hosts are shown in ASCII: an internationalized domain name appears as punycode (
xn--…), so a look-alike domain cannot pass for a familiar one. Every value can be changed, and going back to the first step to pick another service drops the values that service does not take. - Writes and sends nothing before the user acts. Until Next is chosen on the second step, which checks the connection, or a sign-in is started, the configuration is not written and no request leaves the machine.
- Creates one new upstream. An import never changes, replaces or deletes an existing upstream, never makes the new one the default and never adds it to a route. When the name is taken, the dialog asks for another. Several upstreams on one address with different keys are normal.
- Handles one link at a time. Links that arrive while the dialog is open are ignored. When a New upstream or Edit upstream dialog is already open, an arriving link is dropped, so a form being filled in is never replaced.
Links from a newer version#
A parameter that the installed app does not know no longer fails silently. The app shows Cannot import this link, names the parameter, and offers to install an update when one is available. The web form behaves the same way: it lists the settings it recognizes, states that a newer ThinkWatch Lite is needed, and still offers to open the link. A parameter name is shown only when it consists of up to 32 lowercase letters, digits and underscores.
What a link can never set#
Any web page can open a link, so a link is limited to what a user can judge in the dialog. A link that tries to set one of the following is rejected as a whole.
| Not accepted | Reason |
|---|---|
| Outbound proxy, and what happens when it is unavailable | The sender does not know the user’s network. The user picks them in the dialog. |
AWS profile, and environment variable references (${NAME}) | They would make the app read local credentials or the environment and send them to the link’s host. A link with key=${OPENAI_API_KEY} would send the user’s own key to the relay. |
| Aliases, routes, the default upstream and route weights | They would redirect clients that are already configured. |
| The disabled switch and the access token used only for checking | A new upstream starts enabled, and the access token is obtained with the refresh token. |
| Credentials for an account sign-in | The sign-in happens in the app, with the service. |
| Authentication, cookie and connection headers | A header must not replace the key or the identity the gateway sends. |
Plain http:// to public hosts | The key would cross the internet unencrypted. On this computer and private networks, plain http:// is accepted and marked as unencrypted. |
| MCP servers and client configuration | They run commands or change the settings of other applications. |
| Icons and other presentation | Not needed to connect, and a way to imitate a familiar service. |
These settings can be changed in the app after the upstream is created.
Examples#
A relay that needs two headers, limits concurrency to 4, allows 10,000,000 tokens every 5 minutes and $20 a day, lists only some of its models, and charges 0.8 times the public prices with one override (no service: the address is recognized as a custom upstream):
thinkwatch://import?name=example-relay&url=https%3A%2F%2Fapi.relay.example%2Fv1&protocol=openai-chat&key=sk-relay-EXAMPLE&headers=X-Relay-Group%3A%20pro%0AX-Relay-Region%3A%20hk&concurrency=4&limits=minute%7Ctokens%7C10000000%7C5%3Bday%7Ccost%7C20&models=glm-5%2Cglm-5-air&specs=glm-5-air%7C128000%7C96000%7Cy%7Cn&price_multiplier=0.8&prices=glm-5%7C1%7C3.2%7C0.2
The same link in its web form:
https://thinkwat.ch/import#name=example-relay&url=https%3A%2F%2Fapi.relay.example%2Fv1&protocol=openai-chat&key=sk-relay-EXAMPLE&headers=X-Relay-Group%3A%20pro%0AX-Relay-Region%3A%20hk&concurrency=4&limits=minute%7Ctokens%7C10000000%7C5%3Bday%7Ccost%7C20&models=glm-5%2Cglm-5-air&specs=glm-5-air%7C128000%7C96000%7Cy%7Cn&price_multiplier=0.8&prices=glm-5%7C1%7C3.2%7C0.2
Amazon Bedrock in eu-west-1 with access keys:
thinkwatch://import?service=bedrock&auth=aws-keys®ion=eu-west-1&aws_access_key_id=AKIAIOSFODNN7EXAMPLE&aws_secret_access_key=wJalrXUtnFEMI%2FK7MDENG%2FbPxRfiCYEXAMPLEKEY
A ThinkWatch Enterprise gateway. The Add to ThinkWatch Lite button in the ThinkWatch Enterprise console writes this link with the gateway’s address and the new key:
thinkwatch://import?service=thinkwatch&name=thinkwatch&url=https%3A%2F%2Fai.example.com&key=tw-EXAMPLE
A ChatGPT account. The link opens the sign-in; it carries no credential:
thinkwatch://import?service=openai&auth=account&name=chatgpt
A link written for earlier versions, still valid:
thinkwatch://import?name=example-relay&url=https%3A%2F%2Fapi.relay.example&protocol=anthropic&key=sk-relay-EXAMPLE
Links written for earlier versions keep working even where the recognized service would not take them: an official address with another path such as https://api.openai.com/v1, an Ollama address with a key, or https://api.deepseek.com with protocol=openai-chat. Such a link becomes a custom upstream, as it did before.
On a New API site#
A New API or One API site can offer ThinkWatch Lite for every key without changing its code. In the site’s system settings, add a chat preset named ThinkWatch Lite with this URL:
thinkwatch://import?service=newapi&url={address}&key={key}
The site fills in its server address and the key of the row, and the preset appears in the chat menu of each key. Opening it needs ThinkWatch Lite 2026.10.16 or later; with an earlier version, or without the app, nothing happens.
Generated by Lite#
From 2026.10.25, ThinkWatch Lite can also write an import link for an existing upstream, to hand it to another device or another person. In the ⋯ menu of an upstream row, Share… follows View traffic and opens the share dialog:
- The dialog shows the link in its web form. Copy web link and Copy thinkwatch:// link copy each form. On a device without Lite, the web link shows how to install it, and nothing after
#is sent to the site’s server. - Include keys is on by default, and the dialog then notes: “The link contains the key. Anyone holding the link can use the key; send it only through trusted channels.” With it off, the link carries no key, and the recipient fills it in in the dialog.
- The foot of the dialog lists what the link includes and what it leaves out, for example “Includes: address, protocol, key, 2 models, balance source. Leaves out: outbound proxy, routes.”
What the link carries:
| Content | Written | Notes |
|---|---|---|
Service, authentication method, site, name, address, protocol (service, auth, site, name, url, protocol) | Yes | — |
Concurrency limit, usage limits, client identity forwarding, balance source, model list, the models it is limited to, specs set by hand, billing mode, price multiplier, prices (concurrency, limits, client_identity, balance, models, models_only, specs, billing, price_multiplier, prices) | Yes | Values equal to the defaults are left out |
| Key, headers, AWS access keys, OAuth credentials | As set by Include keys, on by default | A value written as ${NAME} cannot be written, since links do not accept that form; it is left out, with the reason |
| Outbound proxy, aliases, routes, weights, the disabled state, AWS profile | No | Import links do not accept them; see What a link can never set |
| ChatGPT and Z.ai / BigModel account sign-ins | No | The link carries the sign-in method only; the recipient stops at the sign-in step and signs in |
| Tiered prices above 200K; values with characters links do not accept | No | Listed under “Leaves out” at the foot of the dialog |
When the link would be longer than 8192 characters, usually because of many prices or specs, the link form is unavailable; the dialog gives the link’s length and suggests a config pack instead, as it does for an upstream that cannot be written as an import link. Export as config pack… opens the export dialog with the upstream checked, with passphrase encryption optional and off by default. The recipient imports it with Import config…, and the pack can also carry what a link cannot set, such as the upstream’s outbound proxy; see Sync and backup.
Links are written by the same rules they are read by: a generated link opened in the same version of the app fills in the New upstream dialog with the settings of the original upstream, apart from what the table above leaves out.
Keys in links#
A link that contains a key, a refresh token or a secret access key is a credential. Generate a separate link for each customer and deliver it through a private channel, such as the customer’s own dashboard. A link without a key leaves the key field empty, to be filled in the dialog.
Link builder
Runs in the browser; nothing is sent.
Choose the service and fill in the settings to generate the links.
App link
Web link