---
title: Sign in from a script
description: Sign a script with no MCP client in to one of Turn Zero's own products with the device grant, and get its product token with no local listener.
slug: /guides/sign-in-from-a-script
order: 38
visibility: public
redirects_from: []
---

# Sign in from a script

Prompt:

> Give this script a token for one of Turn Zero's own products without pasting one into the chat.

Also works:

- "Sign the cron job in to the product from a terminal."
- "I have no MCP client on this machine. How does a script get a token?"
- "Print me a code I can approve in the browser."

Some of Turn Zero's own products sign you in with a [product token](/concepts/sign-in-sessions-and-tokens#product-tokens). An MCP client gets one by opening the sign-in in your browser. A script with no MCP client gets one with the device grant instead. The script registers a public client and prints a code and a link, opening the link where its computer has a browser. You allow it, and the script's next poll prints the token. Nothing listens on your computer, and the token reaches no chat.

## What your tool does

- Registers a public client at `/oauth/register` with a name you will recognise on the approval page ([step 1](#1-register-the-script-as-a-client)).
- Requests the codes at `/oauth/device_authorization`, naming the product's endpoint as the resource ([step 2](#2-request-the-codes)).
- Prints the code and the link, opens the link in a browser where its computer has one, and asks you to sign in and allow the request ([step 3](#3-open-the-link-and-allow-it)).
- Polls `/oauth/token` every five seconds under the device-code grant until the answer carries the token ([step 4](#4-poll-for-the-token)).
- Writes the token into the script's secret store, never into a file it commits or into the chat.
- Asks you for the product's endpoint address where the prompt does not name the product.

## What you need

- A Turn Zero account that has the product. The approval page refuses an account without it.
- The product's MCP endpoint address, which its own documentation gives. That address is the `resource` every request names.
- A browser on any computer, for the one approval. It need not be the computer the script runs on.

## Before your AI starts

This section is for your AI coding tool: what it checks and gathers before it begins. You don't need to do these steps yourself.

- The platform origin the script talks to: `https://turnzero.ai`, or `https://dev.turnzero.ai` for the development platform.
- The product's endpoint address for `resource`. The device grant serves the products whose sign-in answers a signed token; it refuses the platform's own MCP endpoint.
- A place the script keeps the token, such as an environment variable set from a secret store.

## Steps

### 1. Register the script as a client

Every client of the sign-in registers itself; nobody issues a client identifier by hand. The script names the device grant as its only `grant_types` and gives no redirect, because the device grant redirects nothing. The answer's `client_id` is used in every later request.

```text
curl -s -X POST https://turnzero.ai/oauth/register -H 'content-type: application/json' -d '{"grant_types":["urn:ietf:params:oauth:grant-type:device_code"],"client_name":"My script"}'
```

The platform deletes a registration a day after nothing references it. A registration your token names stays while the token does, so a script that starts the sign-in again registers again.

### 2. Request the codes

The request names the client, the product's endpoint as `resource`, and the product's scope, or no scope, which selects it. The answer carries `device_code`, which the script keeps and never shows, and `user_code`, which it prints. It also carries `verification_uri`, `verification_uri_complete`, `expires_in` of 600 seconds, and `interval` of 5.

```text
curl -s -X POST https://turnzero.ai/oauth/device_authorization -d client_id=<client_id> -d resource=<product endpoint>
```

### 3. Open the link and allow it

The script prints the code and the complete link. Where its computer has a browser, it also opens the link there: `open` on macOS, `start` on Windows, or `xdg-open` on Linux. Otherwise open the link in a browser on any computer, or open `https://turnzero.ai/oauth/device` and type the code there.

Sign in to Turn Zero as you do on the website. The page then shows the code and the script's registered name, and asks whether to allow it. Check the code against the one the script printed, then press Allow. The request lasts ten minutes. If the page refuses the account you signed in with, press Cancel, and the script stops at its next poll.

### 4. Poll for the token

Once a request has been made, the script asks the token endpoint whether it was allowed. It polls at most once every five seconds, with the same `client_id`.

```text
curl -s -X POST https://turnzero.ai/oauth/token -d grant_type=urn:ietf:params:oauth:grant-type:device_code -d client_id=<client_id> -d device_code=<device_code>
```

Until you allow it, the answer is `{"error":"authorization_pending"}`, status 400. A poll inside the interval gets `slow_down`, and a poll after the ten minutes `expired_token`, each at 400. A request you cancelled answers `access_denied` at 400, including one cancelled on the page that refused your account. Left alone, a refused request answers `authorization_pending` until its ten minutes run out. Once you allow it, the next poll gets the token once, in the answer `access_token`, with `expires_at`, `resource`, and `token_id`. A second poll with the same code is refused, so the script keeps the value from the one answer.

### 5. Use the token

Send the token as the bearer on the product's own endpoint, where its documentation says. On this platform the token reaches `list_tokens`, the public reads, and the few account reads its product's grant names. It lasts 180 days and is not renewed; run the script's sign-in again after. `revoke_token` with its `token_id` ends it early.

## Expected result

The script prints a code such as `BCDF-GHJK` and a link. After you allow the request in the browser, the page says the tool is connected, and the script's next poll prints one token, which `list_tokens` then lists under the script's registered name. A second poll is refused `invalid_grant`.

## Refusals

This table lists the refusals these steps most often meet; the [refusals page](/reference/refusals) lists all of the platform's refusals, with the cause and remedy of each. A backend package's own failures are listed on its package page.

| Refusal | Status | Cause | Remedy |
| --- | --- | --- | --- |
| `invalid_target` | 400 | The `resource` is not a product endpoint the device grant serves, or the request named none. | Name the product's MCP endpoint exactly, as its documentation gives it. |
| `invalid_scope` | 400 | The request asked for a scope that is not the product's. | Leave `scope` out, or name the product's scope. |
| `invalid_client_metadata` | 400 | The registration gave no redirect and did not name the device grant as its only `grant_types`, gave a redirect that is not an HTTPS or loopback URL, or named a `token_endpoint_auth_method` other than `none`. | Register as step 1 shows. |
| `invalid_client` | 401 | No registration names the `client_id`, for example after the daily sweep deleted it. | Register again with step 1, then start the sign-in again. |
| `invalid_grant` | 400 | The device code is unknown, already answered, or named another client, or the sign-in ended before the poll. | Start the sign-in again from step 2. |

The four answers the poll gives while the request stands, `authorization_pending`, `slow_down`, `expired_token`, and `access_denied`, are [step 4](#4-poll-for-the-token)'s; the script handles each as that step says.

## Related

- [Sign-in, sessions, and tokens](/concepts/sign-in-sessions-and-tokens) compares every credential and says what ends a product token.
- [Mint a token](/guides/mint-a-token) mints a token for a script that calls this platform's own actions.
- [Connect Claude Code or Codex](/getting-started/connect-your-tool) describes the sign-in an MCP client runs for you.
- [Glossary](/concepts/glossary) defines the terms used on this page.
