ON THIS PAGE ▾

Developer Docs

Quickstart to production in under 5 minutes.

Quickstart

Install

pip install permitly
npm install permitly

Minimal working example (Python)

from permitly import PermitlyClient, ScopeTemplates

client = PermitlyClient("sk_live_...")

# Step 1 — request consent, get a URL to redirect your user to
result = client.consent.request({
    "end_user_ref": "user_123",
    "scopes": [ScopeTemplates.send_email()],
    "redirect_url": "https://yourapp.com/done"
})
return redirect(result.consent_url)

# Step 2 — after user approves, verify the token your agent receives
payload = client.consent.verify(token)
# payload.scopes contains the approved scopes

# Step 3 — receive webhook events (Flask example)
from flask import request
from permitly import WebhookVerifier

verifier = WebhookVerifier("whsec_...")

@app.route("/webhook", methods=["POST"])
def webhook():
    event = verifier.verify(request.data, request.headers["X-Permitly-Signature"])
    if event["type"] == "consent.approved":
        # grant the agent access
        pass
    return "", 200
You're live in 5 minutes. Get your API key from the dashboard.

Authentication

All API requests require a bearer token. API keys have the prefix sk_live_.

Authorization: Bearer sk_live_abcdef1234567890

Get your key from Settings → API Keys in your dashboard. Keys are shown once — store them securely. We store only the bcrypt hash.

Token Verification

After consent is approved, your agent receives a signed JWT. Verify it server-side before granting access.

POST /v1/consent/verify

{ "token": "eyJhbGci..." }

JWT payload fields

Field Description
sub End user reference (your end_user_ref value)
agt Agent ID
cid Consent request ID (cr_...)
scp Array of approved scope keys
iat Issued at (Unix timestamp)
exp Expires at (Unix timestamp)
jti JWT ID — used for revocation lookup

Python example

payload = client.consent.verify(token)
# Raises PermitlyError on invalid/expired/revoked token

if "calendar.book" in payload.scopes:
    # grant calendar access
    pass

Webhooks

Permitly fires a webhook on every consent lifecycle event. Configure your endpoint in the dashboard under Agents → Webhooks.

Payload shape

{
  "id": "evt_01abc...",
  "type": "consent.approved",  // consent.approved | consent.declined | consent.revoked
  "created_at": "2026-05-26T14:00:00Z",
  "data": {
    "consent_id": "cr_...",
    "agent_id": "ag_calendarbot",
    "end_user_ref": "user_123",
    "scopes": ["calendar.book"],
    "token": "eyJhbGci..."  // only on consent.approved
  }
}

Signature verification

Every request includes X-Permitly-Signature: sha256=<hmac>. Verify it to reject spoofed payloads.

# Python
from permitly import WebhookVerifier
verifier = WebhookVerifier("whsec_...")
event = verifier.verify(request.body, request.headers["X-Permitly-Signature"])
// Node.js
import { WebhookVerifier } from 'permitly'
const verifier = new WebhookVerifier('whsec_...')
const event = verifier.verify(req.rawBody, req.headers['x-permitly-signature'])

Scope Templates

Built-in templates produce a ready-to-use scope dict with sensible defaults. Usage: ScopeTemplates.send_email()

Key Label Risk Reversible
send_email Send email on your behalf HIGH yes
read_email Read email messages HIGH yes
calendar_read Read calendar events MEDIUM yes
calendar_write Create/edit calendar events MEDIUM yes
calendar_book Book appointments LOW yes
files_read Read files and documents MEDIUM yes
files_write Create and edit files HIGH yes
contacts_read Read contact list MEDIUM yes
contacts_write Add or update contacts MEDIUM yes
profile_read Read basic profile info LOW yes

Error Reference

All errors use the same envelope:

{ "error": { "code": "token_expired", "message": "The consent token has expired." } }
HTTP Code Meaning
401 unauthenticated Missing or invalid API key
403 forbidden Lacks permission
404 not_found Consent request ID not found
422 validation_error Request body failed validation
422 token_expired JWT exp claim has passed
422 token_revoked Consent was revoked after token issued
422 token_invalid JWT signature verification failed
429 rate_limit_exceeded Too many requests — back off and retry