Skip to content
BuzzTake
MenuClose
DocumentationClient sign-in links

How to let a client connect an account without sharing a password

Last updated

Add a client's account without asking for the password. You create the account, send the owner a sign-in link, and they sign in themselves in a live browser that BuzzTake runs. BuzzTake keeps the session; the password never reaches you.

You need a BuzzTake API key and your workspace id. The owner needs a phone or a computer, and no BuzzTake login. The link works for 30 minutes.

1. Create the account with the browser method

POST/v1/workspaces/{workspace_id}/accounts

Send connect.method: "browser", or leave connect out: browser is the default. Set a proxy now if the account should use one; the proxy guide has the formats.

curl

curl
curl https://api.buzztake.com/v1/workspaces/$WORKSPACE_ID/accounts \
  -H "Authorization: Bearer $BUZZTAKE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "linkedin",
    "label": "Client: Northwind",
    "connect": { "method": "browser" }
  }'

JavaScript

JavaScript
const res = await fetch(
  `https://api.buzztake.com/v1/workspaces/${process.env.WORKSPACE_ID}/accounts`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BUZZTAKE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      platform: "linkedin",
      label: "Client: Northwind",
      connect: { method: "browser" },
    }),
  },
);
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const { account, action, sign_in_url } = await res.json();
console.log(account.id, action.id);
console.log("Send this link to the owner:", sign_in_url);

Python

Python
import os
import requests

resp = requests.post(
    f"https://api.buzztake.com/v1/workspaces/{os.environ['WORKSPACE_ID']}/accounts",
    headers={"Authorization": f"Bearer {os.environ['BUZZTAKE_API_KEY']}"},
    json={
        "platform": "linkedin",
        "label": "Client: Northwind",
        "connect": {"method": "browser"},
    },
    timeout=30,
)
if not resp.ok:
    raise SystemExit(f"{resp.status_code}: {resp.text}")
body = resp.json()
print(body["account"]["id"], body["action"]["id"])
print("Send this link to the owner:", body["sign_in_url"])
201 Created (trimmed)
{
  "account": { "id": "0199a1b2-...", "platform": "linkedin", "status": "connecting" },
  "action": { "id": "0199c3d4-...", "type": "connect", "status": "queued", "credits": 25 },
  "sign_in_url": "https://buzztake.com/connect/bts_..."
}

The connect action costs 25 credits when it starts. It then waits at needs_input, kind other, for up to 29 minutes while the owner signs in.

In the web app, open Accounts, select "Add account", pick the platform and choose "Sign in myself". The "Hand off to a person" button copies the same link.

Send sign_in_url by a private channel. Anyone who holds the link can sign in to that one account for 30 minutes, without a BuzzTake login. The link works more than once until it expires, so the owner can open it again on another device.

The owner opens the link and sees a page titled "Sign in to" and the account's name, with three steps:

  1. Sign in as you always do.
  2. Answer any check it asks for.
  3. Press Done.

When BuzzTake has checked the sign-in and saved it, the page says "The account is connected. You can close this page." If the platform sends a code, the owner can type it on the page or in the live browser.

Note: The live browser costs 2 credits per started minute, charged to your organization while the owner uses it. It closes after 30 minutes, or soon after the owner leaves the page.

3. Follow the connection

Read the connect action until it finishes. It leaves needs_input when the owner presses Done and the account is signed in. While it waits for the owner, a read with wait answers at once, so pause between reads.

curl

curl
curl "https://api.buzztake.com/v1/actions/$ACTION_ID?wait=120" \
  -H "Authorization: Bearer $BUZZTAKE_API_KEY"

JavaScript

JavaScript
// A waiting action answers at once, so pause between reads while it waits.
const headers = { Authorization: `Bearer ${process.env.BUZZTAKE_API_KEY}` };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let action;
for (;;) {
  const res = await fetch(
    `https://api.buzztake.com/v1/actions/${actionId}?wait=120`,
    { headers },
  );
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  action = await res.json();
  if (action.status === "needs_input") await sleep(15_000);
  else if (action.status !== "queued" && action.status !== "running") break;
}

console.log(action.status, action.error?.code ?? "");

Python

Python
import os
import time
import requests

# A waiting action answers at once, so pause between reads while it waits.
headers = {"Authorization": f"Bearer {os.environ['BUZZTAKE_API_KEY']}"}
while True:
    resp = requests.get(
        f"https://api.buzztake.com/v1/actions/{action_id}",
        params={"wait": 120},
        headers=headers,
        timeout=130,
    )
    if not resp.ok:
        raise SystemExit(f"{resp.status_code}: {resp.text}")
    action = resp.json()
    if action["status"] == "needs_input":
        time.sleep(15)
    elif action["status"] not in ("queued", "running"):
        break

print(action["status"], (action.get("error") or {}).get("code", ""))
  • succeeded: the account is active. GET /v1/accounts/{id} now shows the handle and display_name the platform reports.
  • failed with challenge_timeout: nobody signed in within 30 minutes. The account is signed_out and this attempt is refunded. Go to step 4.

Over MCP, connect_account with method: "browser" adds the account, and get_sign_in_link returns a link for an existing one.

POST/v1/accounts/{id}/sign-in-links

A new link opens the same account's live browser for another 30 minutes. Earlier links keep working until they expire. When the owner signs in through it and presses Done, the account becomes active again.

Request
curl -X POST https://api.buzztake.com/v1/accounts/$ACCOUNT_ID/sign-in-links \
  -H "Authorization: Bearer $BUZZTAKE_API_KEY"
201 Created
{
  "sign_in_url": "https://buzztake.com/connect/bts_...",
  "expires_at": "2026-10-30T10:30:00Z"
}

Use the same route later, when a session ends and the account needs its owner to sign in again.

Common errors

410 link_expired
The owner opened a link that no longer works. state is expired, revoked or account_deleted. Create a new link unless the account was deleted.
402 insufficient_credits
The balance cannot pay for the sign-in (25 credits), or for the first minute of the live browser.
422 channel_ceiling
"An organization may hold 200 accounts. Delete one to add another."
status: error
"This account is already connected in this workspace." The owner signed in to an account the workspace already has.

See also

To run this on your own accounts, create a BuzzTake account, then add an account and an API key.