Authentication

PortEden uses Access Tokens (API keys) for programmatic access. There are two ways to obtain a key: the Browser Login Flow for CLI tools and SDKs, or Direct Key Generation from the web dashboard.

Base URL

/api/auth/token
Rate limit: 30 req/min100 req/hour

Browser Login Flow

A three-step flow for CLI tools and SDKs: initiate a session, open the browser for authentication, then poll for the API key.

Step 1: Initiate Login

POST/api/auth/token/loginNo Auth

Initiate Login

Creates a login session and returns a URL for the user to open in their browser.

Rate limit: 10 req/min, 30 req/hour

Request Body (optional)

FieldTypeRequiredDescription
operatorIdUUIDNoTarget operator/workspace ID. If omitted, uses user's default operator
keyTitlestringNoFriendly name for the generated key
{
"operatorId": "550e8400-e29b-41d4-a716-446655440000",
"keyTitle": "My CLI Key"
}

Response Fields

FieldTypeDescription
sessionTokenstringSession identifier for polling
pollSecretstringSecret for PKCE-like verification when polling. Keep this client-side only
loginUrlstringURL to open in the user's browser for authentication
expiresAtDateTimeWhen this session expires
messagestringHuman-readable instructions

Step 2: Poll for Completion

GET/api/auth/token/poll/{sessionToken}?secret={pollSecret}No Auth

Poll for Completion

Client polls this endpoint to check if the user has completed browser authentication.

Rate limit: 60 req/min (allows ~1/sec polling), 300 req/hour

Parameters

ParameterInTypeDescription
sessionTokenPathstringSession token from Step 1
secretQuerystringPoll secret from Step 1 (PKCE-like verification)

Response 200 OK

{
"status": "completed",
"apiKey": "pe_k1_abc123..."
}

Status Values

StatusMeaningAction
pendingUser hasn't authenticated yetContinue polling
completedAuthentication successfulExtract apiKey and stop polling
expiredSession timed outStart a new login flow
invalid_secretWrong poll secret (returned as 400)Verify the secret matches Step 1

Important

The apiKey is only returned once when status is completed. Store it securely — it cannot be retrieved again.

Step 3: Web App Callback (Internal)

POST/api/auth/token/callbackJWT Session

Web App Callback

Called by the PortEden web app after the user authenticates in the browser. Not called by external clients directly.

Note

This endpoint is internal to the PortEden web app. External clients do not need to call this endpoint.

Request Body

FieldTypeRequiredDescription
sessionTokenstringYesSession token being completed
operatorIdUUIDNoTarget operator. Falls back to user's default
keyIdintNoExisting key ID to link (must provide with apiKey)
apiKeystringNoExisting API key to link (must provide with keyId)

Response 200 OK

{
"success": true,
"keyId": 42,
"keyTitle": "My CLI Key",
"message": "Authentication successful. You can close this browser window."
}

Direct Key Generation

POST/api/auth/token/generateJWT Session

Generate API Key

Generates an API key directly for authenticated web users — no browser flow needed.

Rate limit: 10 keys per hour

Request Body (all optional)

FieldTypeDefaultDescription
operatorIdUUIDUser's defaultTarget operator/workspace
titlestringAuto-generatedFriendly name for the key
avatarstring"general"Key avatar identifier
expiresInDaysintNeverKey expiration in days from now
masterAccessLevelstring"full_access"Access level
visibleFieldsstring[]All fieldsFields visible when masterAccessLevel is view_filtered
allowedOperationsstring[]All operationsAllowed write operations when masterAccessLevel is full_access
timeframePastDaysintUnlimitedHow many days into the past the token can access
timeframeFutureDaysintUnlimitedHow many days into the future the token can access
{
"operatorId": "550e8400-e29b-41d4-a716-446655440000",
"title": "Production Integration Key",
"avatar": "claude",
"expiresInDays": 90,
"masterAccessLevel": "view_only",
"visibleFields": ["title", "times", "attendees", "location"],
"allowedOperations": ["respond_to_event"],
"timeframePastDays": 30,
"timeframeFutureDays": 60
}

Important

The apiKey value is returned only once at creation time. It cannot be retrieved again. Store it securely.

Using the API Key

Once you have an API key, include it in all requests as a Bearer token:

Authorization: Bearer pe_k1_abc123def456...

Or set the environment variable for CLI/SDK usage:

export PE_API_KEY=pe_k1_abc123def456...

Token Status

GET/api/auth/token/statusBearer Token

Get Token Status

Returns information about the current access token and authenticated user.

Response 200 OK

{
"authenticated": true,
"userId": 123,
"email": "user@example.com",
"userName": "Jane Smith",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"operatorId": "660e8400-e29b-41d4-a716-446655440000",
"operatorName": "Acme Corp",
"keyId": 42,
"keyTitle": "My Integration Key",
"keyAvatar": "claude",
"createdAt": "2026-01-15T10:00:00Z",
"expiresAt": "2026-04-15T10:00:00Z",
"lastUsedAt": "2026-02-07T09:30:00Z",
"masterAccessLevel": "full_access",
"visibleFields": ["title", "times", "attendees", "location", "description", "status", "labels", "join_url", "organizer"],
"allowedOperations": ["respond_to_event", "edit_title", "edit_location", "edit_description", "edit_attendees", "edit_times", "create_events", "delete_events"],
"timeframePastDays": null,
"timeframeFutureDays": null,
"accessRules": [],
"linkedResources": [
{
"resourceId": "aaa-bbb-ccc",
"title": "Primary Calendar"
}
],
"hasLinkedResources": true
}