The agent opens your site in a browser, hits “Sign in with LIME”, and stays on the waiting screen — you create a login request and show login_request_id on that page. The agent reads the ID from the screen and approves.
After approve, the browser has your normal site session for that agent_id.
When the browser hits Sign in with LIME, call create_login_request. Put login_request_id on the waiting screen — that screen is how the agent gets the ID.
import asyncio
from lime_sites import LimeSite
LIME_SITE_TOKEN = "LIME_SITE_TOKEN_HERE"
async def main() -> None:
site = LimeSite(site_token=LIME_SITE_TOKEN)
req = await site.create_login_request()
print("login_request_id:", req.request_id)
# Show req.request_id on the waiting screen in the browser.
await site.aclose()
asyncio.run(main())AssertResponse includes login*request_id (lr*…); waiting UI shows the ID.
Keep GET …/events with X-Site-Token while the page waits. When the agent approves, the event carries the passport JWT for this request_id. Treat **login_request_id as the SSE idempotency key** — Redis delivery is at-least-once; duplicate APPROVED frames for the same id must not mint a second site session.
import asyncio
from lime_sites import LimeSite
LIME_SITE_TOKEN = "LIME_SITE_TOKEN_HERE"
async def main() -> None:
received = asyncio.Event()
box: dict[str, object] = {}
site = LimeSite(site_token=LIME_SITE_TOKEN)
@site.on_login
async def handle_login(request_id: str, passport: str | None) -> None:
box["request_id"] = request_id
box["passport"] = passport
received.set()
await asyncio.wait_for(received.wait(), timeout=120)
print(box.get("request_id"), bool(box.get("passport")))
await site.aclose()
asyncio.run(main())AssertSSE delivers passport JWT for the same login_request_id.
verify_passport (signature, aud=lime-site-login, expected request_id). Read agent_id and set your normal site cookie — same as a human login.
import asyncio
from lime_sites import LimeSite
LIME_SITE_TOKEN = "LIME_SITE_TOKEN_HERE"
PASSPORT_JWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
LOGIN_REQUEST_ID = "lr_xxxxxxxx"
async def main() -> None:
site = LimeSite(site_token=LIME_SITE_TOKEN)
verified = await site.verify_passport(
PASSPORT_JWT,
expected_request_id=LOGIN_REQUEST_ID,
)
agent_id = verified.claims["agent_id"]
# Set YOUR site session cookie for agent_id — LIME does not create it.
print("signed_in_agent_id:", agent_id)
await site.aclose()
asyncio.run(main())AssertClaims.agent_id present; site session cookie issued for that agent_id.
LIME does not push login_request_id to the agent. Show the ID on the waiting screen in the browser — the agent reads it there and calls approve. Site and agent must share one LIME portal account.
showWaitingScreen(login_request_id)| Scope | Status | Code | When | What to do |
|---|---|---|---|---|
| LIME API | 404 | SITE_LOGIN_REQUEST_NOT_FOUND | login_request_id is unknown **or** approve lacks row authority (foreign owner / unknown site) | Create a new request from the button and show the ID on the waiting screen again; use an agent under the same portal account as the site |
| LIME API | 409 | SITE_LOGIN_REQUEST_EXPIRED | Login request TTL elapsed before approve | Ask the user to click Sign in with LIME again |
| SDK verify | 401 | passport_verify_failed | Signature, aud, or request_id check fails in the SDK | Call LimeSite.verify_passport with the same request_id from create |