> For the complete documentation index, see [llms.txt](https://docs.tiun.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tiun.io/guides/authentication/get-user-info.md).

# Get user info server-side

Sometimes your backend needs to know a user's current state while the user isn't active in your app — a scheduled job checking whether a subscription is still active, an external service acting on the user's behalf, or any async flow that runs without a browser session. The UserInfo API covers this: your server fetches a tiun user's current `email` and `productAccess` using only their **`userId`** and your API key. No token, no user present.

***

## User info vs. verification

[Server-side authentication verification](/guides/authentication/verify-server-side.md) answers "**is this request really coming from a signed-in user?**" — it exchanges a short-lived token and returns `isAuthenticated`.

The UserInfo API answers "**what is the current state of a user I already know?**" — a plain lookup by ID. It doesn't authenticate anyone: anyone with your API key and a userId gets the data. Use it for server-initiated flows, and keep using verification to gate incoming requests from your frontend.

***

## How it works

1. Capture the tiun **`userId`** while the user is in your app — from the [user object](/reference/authentication/user-object.md) in a `userChange` payload, or from `userInfo.userId` on a verified request — and store it with your own user record.
2. Later, your server calls the [tiun UserInfo API](/api-reference/user-info.md) with that ID and your API key.
3. The response carries the user's current `userId`, `email`, and `productAccess` — check `productAccess` for the product you care about.

***

## Setup: API key

The endpoint is protected by an API key — the same one used for server-side verification. If you don't have one yet, create it in the dashboard: open **APIs** in the sidebar and click **Create new key**. Store it securely in your backend environment variables.

***

## Call the endpoint

**Endpoint:**

`GET /live_api/s2s/v1/users/{userId}/info`

**Base URLs:**

| Environment | URL                             |
| ----------- | ------------------------------- |
| Live        | `https://api.tiun.live`         |
| Sandbox     | `https://api-sandbox.tiun.live` |

Use the base URL and API key from the **same environment** the userId belongs to — live and sandbox users are separate, and API keys are not shared between environments.

**Header:** `X-TIUN-API-KEY: <your-api-key>`

No request body — the userId travels in the path.

**Response codes:**

| Status | Meaning                              |
| ------ | ------------------------------------ |
| `200`  | User object returned — read the body |
| `400`  | The request is invalid               |
| `401`  | API key is invalid                   |
| `404`  | The user does not exist              |

A `200` response carries the user object:

```json
{
  "userId": "u-...",
  "email": "user@example.com",
  "productAccess": ["p-live-pro"]
}
```

Unlike the verification response, there is no `isAuthenticated` wrapper — the lookup isn't tied to a session, so the body is just the user's current state.

***

## Full example

A reusable check your backend can run from anywhere — a scheduled job, a queue worker, or a service handling a user who isn't in the app right now:

```javascript
const BASE_URL = process.env.TIUN_API_BASE || 'https://api-sandbox.tiun.live';
const API_KEY = process.env.TIUN_API_KEY;

async function hasActiveSubscription(userId, productId) {
  const response = await fetch(
    `${BASE_URL}/live_api/s2s/v1/users/${userId}/info`,
    {
      headers: { 'X-TIUN-API-KEY': API_KEY },
    },
  );

  if (response.status === 404) {
    // unknown user → no access
    return false;
  }

  if (response.status !== 200) {
    // API key invalid or upstream error → fail closed
    return false;
  }

  const user = await response.json();
  return user.productAccess.includes(productId);
}

// e.g. in a background job:
const isActive = await hasActiveSubscription('u-abc123', 'p-live-pro');
```

`productAccess` reflects what the user has paid for **right now** — tiun keeps it consistent through renewals, cancellations, and payment failures, so there is nothing to cache or reconcile on your side. See [Product access](/reference/checkout/product-access.md) in Reference.

***

{% hint style="info" %}
Live and sandbox are independent environments — each has its own API base URL and API keys. Use sandbox credentials while your app runs with `sandbox: true`; switch URL and key together when you ship live traffic. See [Sandbox](/reference/generic/sandbox.md).
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tiun.io/guides/authentication/get-user-info.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
