Third-party integrations

Use the OAuth-style authorization flow to connect organizer accounts and read event feed data from the API with a scoped bearer token.

1) Redirect users to authorize

Send organizers to Kagibag with your app name, callback URL, and a random state value.

GET /oauth/authorize?client_name=MyApp&redirect_uri=https%3A%2F%2Fexample.com%2Fkagibag%2Fcallback&state=csrf_token_123
client_name: Display name shown on the consent screen.redirect_uri: Your callback URL that receives success/error params.state: Your anti-CSRF token. Validate it on callback.

2) Handle callback results

Kagibag redirects back to your redirect_uri with either a token payload or an error payload.

Success query params

  • token: Sanctum bearer token scoped to events:read
  • event_feed_url: Organizer feed endpoint URL
  • state: Echoed value from your original request

Error query params

  • error: access_denied
  • error_description: Reason (for example, user denied or not organizer)
  • state: Echoed value from your original request

3) Read organizer event feeds

Use the returned token as a bearer token on the organizer event endpoints.

curl -H "Authorization: Bearer {token}" "{event_feed_url}"
curl -H "Authorization: Bearer {token}" "https://app.kagibag.test/api/v1/organizer/{team_slug}/events/{event_slug}"
List events: GET /api/v1/organizer/{team:slug}/eventsEvent detail: GET /api/v1/organizer/{team:slug}/events/{slug}Auth scope: tokens can only access the issuing organizer's team feed.

Security checklist

  • Generate a unique, random state per authorization request and verify it on callback.
  • Use HTTPS callback URLs in production only.
  • Treat the token like a secret and store it encrypted at rest.
  • Never expose tokens in client-side logs, analytics, or browser storage when avoidable.
  • Handle denied/error callbacks gracefully and prompt users to retry if needed.

Example callback handler

if (query.error) {
  assert(query.state === session.oauth_state);
  return showError(query.error_description ?? 'Authorization failed');
}

assert(query.state === session.oauth_state);
saveKagibagToken(query.token);
saveKagibagFeedUrl(query.event_feed_url);
return syncEventsNow();