> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bondify.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser & Mobile SDK (REST)

> Add Telegram sign-in to any client without a framework SDK, using Bondify's public REST endpoints.

There is no embedded `<script>` widget to load. For browsers and native mobile apps, Bondify exposes two **public endpoints** that need only your `project_id` — no secret key on the client. This is what the React and Flutter SDKs call under the hood, and what you call directly when you have no SDK for your stack.

<Note>
  Enable **Mobile SDK** under **Project → Settings** to turn on the public endpoints. Without it, `/generate/public` and `/verify/public` return `PUBLIC_ACCESS_DISABLED`.
</Note>

## Endpoints

Base URL: `https://api.bondify.dev`

### `POST /api/v1/generate/public`

Create a pending session.

```json theme={null}
// Request
{ "project_id": "proj_xxxxxxxx" }

// Response — 200
{
  "deeplink": "https://t.me/your_bot?start=a1b2c3d4e5f6",
  "session_token": "a1b2c3d4e5f6",
  "expires_at": 1700000600000
}
```

### `POST /api/v1/verify/public`

Poll for the session result. On the first `confirmed` read, the session becomes `used`.

```json theme={null}
// Request
{ "project_id": "proj_xxxxxxxx", "session_token": "a1b2c3d4e5f6" }

// Response — one of:
{ "status": "pending" }
{ "status": "confirmed", "telegram_id": "123", "telegram_name": "Alex",
  "telegram_username": "alex", "telegram_phone": null,
  "proof": "eyJhbGci…", "confirmed_at": 1700000123456 }
{ "status": "cancelled" }
{ "status": "expired" }
```

## Recommended client loop

<Steps>
  <Step title="Generate">
    Call `/generate/public`. Keep the `session_token`.
  </Step>

  <Step title="Open the deep link">
    Open `deeplink` (new tab on web, `url_launcher`/`Linking` on mobile).
  </Step>

  <Step title="Poll">
    Call `/verify/public` every \~2 seconds. Stop on `confirmed`, `cancelled`, or `expired`. The session's TTL is 10 minutes; you'll typically also stop polling after a couple of minutes of inactivity.
  </Step>

  <Step title="Verify on your server">
    Send the `proof` to your backend and [verify it](/guides/backend-verification/backend-verification) with your `webhook_secret`.
  </Step>
</Steps>

```js theme={null}
const PROJECT_ID = 'proj_xxxxxxxx'
const API = 'https://api.bondify.dev'

async function signInWithTelegram() {
  const { deeplink, session_token } = await fetch(`${API}/api/v1/generate/public`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ project_id: PROJECT_ID }),
  }).then(r => r.json())

  window.open(deeplink, '_blank')

  return new Promise((resolve, reject) => {
    const poll = setInterval(async () => {
      const res = await fetch(`${API}/api/v1/verify/public`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ project_id: PROJECT_ID, session_token }),
      }).then(r => r.json())

      if (res.status === 'confirmed') { clearInterval(poll); resolve(res) }
      if (res.status === 'cancelled') { clearInterval(poll); reject(new Error('cancelled')) }
      if (res.status === 'expired')   { clearInterval(poll); reject(new Error('expired')) }
    }, 2000)
  })
}
```

## Error responses

Public endpoints return a `4xx` with `{ "error": "…" }` for these conditions:

| Condition                | Meaning                                                               |
| ------------------------ | --------------------------------------------------------------------- |
| `PUBLIC_ACCESS_DISABLED` | Enable **Mobile SDK** in project settings                             |
| `PROJECT_NOT_FOUND`      | Unknown `project_id`                                                  |
| `PROJECT_INACTIVE`       | The project is paused                                                 |
| Rate limited (`429`)     | Public generation is capped per IP+project and per project — back off |

## Prefer an SDK when you have one

If you're on React/Next.js or Flutter, use the official SDK instead of calling these endpoints by hand — it manages the generate/poll/expiry lifecycle, timeouts, and state for you:

<CardGroup cols={3}>
  <Card title="React / Next.js" icon="react" href="/guides/react/react"><code>@bondify/react</code></Card>
  <Card title="Flutter" icon="mobile" href="/guides/flutter/flutter"><code>bondify\_flutter</code> — GitHub</Card>
  <Card title="REST API reference" icon="server" href="/sdk-reference/rest-api/rest-api">All endpoints</Card>
</CardGroup>
