Skip to main content
Bondify’s authentication flow replaces passwords and SMS codes with a single tap inside Telegram. When a user wants to sign in, your app creates a session, opens Telegram via a deeplink, and waits while Bondify’s bot asks the user to confirm. Once the user taps ✅ Confirm, your app receives a cryptographically signed proof that you verify server-side before creating a user session. The entire exchange typically completes in under 10 seconds.

The main flow

1

User taps the sign-in button

In your app — whether a web page, mobile screen, or any other surface — the user taps your “Sign in with Telegram” button. This triggers a call to generate a new Bondify session. If you are using an SDK, the SDK handles the next two steps automatically.
2

Your app creates a session

Call POST /api/v1/generate/public with your project_id:
curl -X POST https://api.bondify.io/api/v1/generate/public \
  -H "Content-Type: application/json" \
  -d '{"project_id": "proj_xxxxxxxx"}'
Bondify responds with a session_token and a Telegram deeplink:
{
  "session_token": "sess_abc123",
  "deeplink": "https://t.me/BondifyAuthBot?start=sess_abc123"
}
Store the session_token — you will need it for polling and proof verification.
3

Telegram opens

Your app opens the deeplink in Telegram (the SDK does this automatically). Bondify’s bot sends the user a confirmation message showing your project name and the data it is requesting (profile info, phone number, etc.).
4

User confirms or cancels

Inside Telegram, the user taps either ✅ Confirm to approve the login or ❌ Cancel to decline. No passwords, no SMS — just one tap.
5

Your app polls for the result

While Telegram is open, your app polls POST /api/v1/verify/public every 2 seconds, passing the session_token:
curl -X POST https://api.bondify.io/api/v1/verify/public \
  -H "Content-Type: application/json" \
  -d '{"project_id": "proj_xxxxxxxx", "session_token": "sess_abc123"}'
While the user is deciding, you receive:
{ "status": "pending" }
Once the user confirms, the response includes the full session data and a signed proof:
{
  "status": "confirmed",
  "telegram_id": "123456789",
  "telegram_name": "Alex Johnson",
  "telegram_username": "alexj",
  "proof": "eyJhbGci..."
}
Stop polling as soon as you receive any terminal status: confirmed, cancelled, expired, or used.
6

Your server verifies the proof

Send the proof string to your backend and call verifyProof() from @bondify/server:
import { verifyProof } from '@bondify/server';

// In your POST /api/session handler:
const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY);
// user.id, user.telegramId, user.name are now safe to trust
verifyProof() validates the HMAC-SHA256 signature against your project’s secret key. If the proof is invalid or tampered with, it throws — reject the request with a 401.
7

Create your app session

Once verifyProof() returns successfully, you have a verified Telegram identity. Create a cookie, JWT, or database session as you normally would:
// Next.js Server Action example
'use server';
import { verifyProof } from '@bondify/server';
import { cookies } from 'next/headers';

export async function createSession(proof: string) {
  const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY!);
  (await cookies()).set('session', await issueJWT(user));
}

Webhook alternative

Instead of polling, you can configure a Webhook URL on your project to receive real-time push events from Bondify. When the user confirms or cancels in Telegram, Bondify immediately sends a POST request to your endpoint.
// auth.confirmed event
{
  "event": "auth.confirmed",
  "session_token": "sess_abc123",
  "telegram_id": "123456789",
  "telegram_name": "Alex Johnson",
  "telegram_username": "alexj",
  "confirmed_at": 1700000000000
}

// auth.cancelled event
{
  "event": "auth.cancelled",
  "session_token": "sess_abc123",
  "cancelled_at": 1700000000000
}
Every webhook request includes an X-Bondify-Signature header. Always verify this signature before processing the event:
import crypto from 'crypto';

// Use the raw request body string — not re-serialised JSON
const sig = req.headers['x-bondify-signature'] as string;
const expected = crypto
  .createHmac('sha256', process.env.BONDIFY_SECRET_KEY!)
  .update(rawBody) // rawBody = req.body.toString() before JSON parsing
  .digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
  return res.status(401).end();
}
For full webhook setup instructions, see Webhooks.

Error handling

When status === 'cancelled', stop polling immediately and display a user-friendly message:
if (result.status === 'cancelled') {
  stopPolling();
  showMessage('You declined the Telegram login request. Try again when you\'re ready.');
}
Never trust telegram_id or telegram_name values passed directly from the client without verifying the proof server-side first. Client-supplied values can be fabricated. Only values returned by a successful verifyProof() call are trustworthy.

Flow summary

User taps button


POST /api/v1/generate/public  ──►  { session_token, deeplink }


Open deeplink in Telegram


User taps ✅ Confirm (or ❌ Cancel)


Poll /api/v1/verify/public every 2s

      ▼  status === "confirmed"
Receive { proof, telegram_id, telegram_name, ... }


verifyProof(proof, secretKey)  ──►  verified user object


Issue app session (cookie / JWT)

Next steps

Projects

Set up your project ID and secret key before making your first API call.

Security

Review the security model and best practices for a safe integration.