docs.pbx.lt

Auth

Authentication

The pbx-billing API uses bearer JWTs. Customers go through a magic-link first-login flow on signup; subsequent sessions use email + password. The admin surface additionally requires TOTP 2FA. The sections below walk through the complete flow with copy-pasteable curl examples.

  1. 1. Sign up (customer)

    Signup creates a draft account and emails a magic link to verify the address. The magic link is single-use and expires in 24 hours.

    curl -X POST https://billing-api.pbx.lt/v1/signup \
      -H 'content-type: application/json' \
      -d '{
        "email": "owner@example.com",
        "company_name": "Acme Ltd",
        "country": "LT",
        "plan_tier": "starter"
      }'

    On success: HTTP 202 with { "signup_id": "..." }. The email arrives within seconds.

  2. 2. Redeem the magic link (first login)

    The link lands you on billing.pbx.lt, which redeems the token and prompts you to set a password. The raw redemption endpoint:

    curl -X POST https://billing-api.pbx.lt/v1/auth/magic-link/redeem \
      -H 'content-type: application/json' \
      -d '{
        "token": "MAGIC_LINK_TOKEN_FROM_EMAIL",
        "password": "<new-strong-password>"
      }'

    On success: HTTP 200 with { "access_token": "...", "refresh_token": "...", "expires_in": 900 }.

  3. 3. Sign in (returning customer)

    curl -X POST https://billing-api.pbx.lt/v1/auth/sign-in \
      -H 'content-type: application/json' \
      -d '{
        "email": "owner@example.com",
        "password": "<password>"
      }'

    On failure: HTTP 401 with a problem-details body whose type is urn:codus-nullus:pbx-billing:problem:auth.invalid_credentials.

  4. 4. Sign in (admin, 2FA-gated)

    Admin sessions require an enrolled TOTP authenticator. The flow is two-step: post credentials, then post the 6-digit code with the challenge id from the first response. Both happen in the admin.pbx.lt sign-in screen.

    # Step 1: post credentials, get a 2FA challenge
    curl -X POST https://billing-api.pbx.lt/v1/auth/admin/sign-in \
      -H 'content-type: application/json' \
      -d '{
        "email": "admin@codus-nullus.com",
        "password": "<password>"
      }'
    # -> 200 { "challenge_id": "...", "expires_in": 300 }
    
    # Step 2: post the TOTP code
    curl -X POST https://billing-api.pbx.lt/v1/auth/admin/2fa/verify \
      -H 'content-type: application/json' \
      -d '{
        "challenge_id": "...",
        "code": "123456"
      }'
    # -> 200 { "access_token": "...", "refresh_token": "...", "expires_in": 900 }

    On invalid code: HTTP 401, problem-details type urn:codus-nullus:pbx-billing:problem:auth.totp_invalid.

  5. 5. Call a protected endpoint

    curl https://billing-api.pbx.lt/v1/me \
      -H 'authorization: Bearer ACCESS_TOKEN'
  6. 6. Refresh the access token

    Access tokens expire after 15 minutes. Refresh tokens last 30 days and rotate on every refresh - store the new refresh token and discard the old one on every call.

    curl -X POST https://billing-api.pbx.lt/v1/auth/refresh \
      -H 'content-type: application/json' \
      -d '{ "refresh_token": "REFRESH_TOKEN" }'
  7. JWT shape

    • sub: user id (UUID)
    • acc: account id (UUID)
    • roles: array of customer | admin | support
    • iat / exp: exp - iat = 900 seconds
    • twofa: true for admin sessions that completed step 2 above; absent otherwise
  8. Idempotency-Key (required for mutating endpoints)

    Every mutating endpoint that touches money, subscriptions, or upstream pbx-core provisioning requires a client-generated Idempotency-Key header. We store the first response for 24 hours and replay it on retry. See the reference for the per-endpoint requirement.

    curl -X POST https://billing-api.pbx.lt/v1/payments/topup \
      -H 'authorization: Bearer ACCESS_TOKEN' \
      -H 'content-type: application/json' \
      -H 'idempotency-key: 9b5c2e3a-4d7e-4f8a-9b6c-1a2b3c4d5e6f' \
      -d '{
        "amount_minor": 5000,
        "currency": "EUR",
        "save_card": true
      }'

    Use a UUID v4 per logical operation. Same key + same body returns the original response; same key + different body returns HTTP 409.

  9. Sign out

    curl -X POST https://billing-api.pbx.lt/v1/auth/sign-out \
      -H 'authorization: Bearer ACCESS_TOKEN'