01 - Authentication & Team


1. Authentication flow at a glance

sequenceDiagram
    autonumber
    participant Dev as Developer App
    participant API as DigetPay API v1
    participant Notify as SMS / Email OTP

    Dev->>API: POST /developer-portal/register
    API-->>Notify: Send OTP
    API-->>Dev: 201 - {challengeId, expiresIn, email}

    Dev->>API: POST /developer-portal/register/verify-otp
    API-->>Dev: 200 - {ok: true, verified: true}

    Dev->>API: POST /developer-portal/auth/login
    API-->>Notify: Send OTP
    API-->>Dev: 200 - {challengeId, expiresIn, email}

    Dev->>API: POST /developer-portal/auth/login/verify-otp
    API-->>Dev: 201 - {accessToken, refreshToken, tokenType, expiresIn, user, organization}

    Note over Dev,API: Access token valid for 12 hours

    Dev->>API: GET /developer-portal/auth/me - Bearer token
    API-->>Dev: 200 - {user, organization, applicationCount}

    Dev->>API: POST /developer-portal/auth/refresh
    API-->>Dev: 200 - pair rotated

    Dev->>API: POST /developer-portal/auth/logout
    API-->>Dev: 200 - {ok: true}

2. Token contract

PropertyValue
Access tokenJWT, typ=developer-portal-access, validity 43200 s (12 h)
Refresh tokenOpaque string, 30 days, rotated on every /refresh
Auth headerAuthorization: Bearer <accessToken>

3. Registration

Creates the organization (ONBOARDING) + owner + verification (DRAFT)
atomically.

{
  "companyName": "Acme Payment Solutions",
  "legalName": "Acme Payment Solutions LLC",
  "email": "[email protected]",
  "phone": "+966501234567",
  "fullName": "Sarah Developer",
  "password": "SecurePass123"
}

Response 201

{
  "challengeId": "a1b2c3d4e5f60718293a4b5c",
  "expiresIn": 600,
  "email": "[email protected]"
}

{ "challengeId": "a1b2c3d4e5f60718293a4b5c", "otp": "123456" }

Response 200

{ "ok": true, "verified": true }

{ "challengeId": "a1b2c3d4e5f60718293a4b5c" }

Response 200

{ "expiresIn": 600 }

Registration does not auto-login. After OTP verification, route your user
through the login flow to obtain tokens.


The registrar (the OWNER) is created ACTIVE immediately — it never passes
through the PENDING state that invited DEVELOPER members do.



4. Login

{ "email": "[email protected]", "password": "SecurePass123" }

Response 201 — same challenge shape as registration:

{ "challengeId": "a1b2c3d4e5f60718293a4b5c", "expiresIn": 600, "email": "[email protected]" }

{ "challengeId": "a1b2c3d4e5f60718293a4b5c", "otp": "123456" }

Response 201 — save the tokens:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
  "refreshToken": "4f6b2a1c8e9d0f1a2b3c4d5e6f708192",
  "tokenType": "Bearer",
  "expiresIn": 43200,
  "user": { "id": "10", "email": "[email protected]", "name": "Sarah Developer" },
  "organization": {
    "id": "42",
    "name": "Acme Payment Solutions",
    "legalName": "Acme Payment Solutions LLC",
    "status": "ONBOARDING"
  }
}

5. Session endpoint — GET /developer-portal/auth/me

Returns profile + organization + application count. Used to confirm a live
session.

{
  "user": { "id": "10", "email": "[email protected]", "name": "Sarah Developer", "role": "OWNER" },
  "organization": { "id": "42", "name": "Acme Payment Solutions", "status": "APPROVED" },
  "applicationCount": 3
}

The role tells you what the account can do: an OWNER is a company admin with
full control; a DEVELOPER can build and maintain the integration but cannot
edit company data, manage the marketplace listing, view refunds, open tickets, or
manage team members.


6. Refresh & logout

{ "refreshToken": "4f6b2a1c8e9d0f1a2b3c4d5e6f708192" }

Response 200 — pair rotated, previous refresh token revoked:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
  "refreshToken": "0a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "tokenType": "Bearer",
  "expiresIn": 43200
}

{ "refreshToken": "0a1b2c3d4e5f60718293a4b5c6d7e8f90" }

Response 200 — { "ok": true }


7. Password recovery

MethodEndpointPurpose
POST/developer-portal/auth/forgot-passwordStart reset (OTP challenge)
POST/developer-portal/auth/forgot-password/verifyVerify OTP → resetToken
POST/developer-portal/auth/forgot-password/resetSet the new password

8. Authentication error reference

StatusCode / scenarioGuidance
400Invalid phone (Saudi +9665…), weak passwordRe-validate inputs client-side
400Invalid or expired activation linkAsk the owner to resend a fresh link
401Wrong password, unknown user → generic login errorDo not reveal which one
401Token typ ≠ developer-portal-accessRefresh or re-login on the portal
404Order/app not found, or login/direct in production404 is the correct behaviour
409Account already exists on registrationRoute to login instead
409Activation: account already activatedUse the forgot-password flow
429OTP rate limit exceededWait and retry with backoff

9. Team management & roles

Every developer organization starts as a single OWNER account created by
self-registration. The OWNER can invite colleagues as DEVELOPER members who
help with the integration work.

PropertyValue
RolesOWNER (full control), DEVELOPER (integration work)
Team operationsOwner-only
Invite expiry72 hours
Main endpointsGET /team, POST /team/invite, POST /team/:userId/resend-invite, DELETE /team/:userId, POST /auth/activate

Team endpoints require a valid developer-portal Bearer JWT. The activation
endpoint is public — it needs no token.


9.1 Roles

RoleCreated byWhat they can do
OWNERSelf-registrationEverything, including team management
DEVELOPEROwner inviteIntegration work

Only an OWNER can manage team members; invites always create a DEVELOPER
account — the team API can never create or reassign an OWNER.

CapabilityOwnerDeveloper
Dashboard, company profile, applications, credentials, webhooks, API logs, orders, certification✔✔
Edit company profile & verification, upload documents✔—
Marketplace storefront listing (offers, panels, images)✔—
Refunds and support tickets✔—
Manage team members (invite, resend, remove)✔—

GET /auth/me returns the member's role, so your app can tailor its UI.

A DEVELOPER cannot edit the company profile, manage the marketplace listing,
view refunds, open support tickets, or manage team members.


9.2 Team lifecycle

stateDiagram-v2
    [*] --> PENDING : owner invites
    PENDING --> ACTIVE : activation link + password
    PENDING --> [*] : invite expires / removed
    ACTIVE --> INACTIVE : owner removes
StatusMeaning
PENDINGInvited; no password set yet. Cannot log in.
ACTIVEPassword set and account active. Can log in.
INACTIVEDeactivated by the owner. Sessions revoked.

The OWNER is ACTIVE from self-registration — it never passes through PENDING.

9.3 Invite → activate → login

  1. Owner sends POST /developer-portal/team/invite with email and fullName;
    the server creates a PENDING member and emails an activation link.
  2. The invitee clicks the link and sets their password via
    POST /developer-portal/auth/activate with the token and newPassword;
    the account becomes ACTIVE.
  3. The invitee signs in through the normal login flow and receives a JWT with
    the DEVELOPER role.

9.4 API reference

List members — GET /developer-portal/team — Bearer, owner-only

Returns every member: the OWNER plus all invited DEVELOPER accounts.

curl -X GET \
  "https://fin-api.digetpay.com/v1/developer-portal/team" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Response 200 — TeamMember[]

[
  {
    "id": "10",
    "email": "[email protected]",
    "name": "Sarah Developer",
    "role": "OWNER",
    "status": "ACTIVE",
    "invitedByUserId": null,
    "inviteExpiresAt": null,
    "lastLoginAt": "2026-08-20T09:00:00.000Z",
    "createdAt": "2026-07-01T08:00:00.000Z"
  },
  {
    "id": "11",
    "email": "[email protected]",
    "name": "Ali Mohammed",
    "role": "DEVELOPER",
    "status": "PENDING",
    "invitedByUserId": "10",
    "inviteExpiresAt": "2026-08-29T10:00:00.000Z",
    "lastLoginAt": null,
    "createdAt": "2026-08-26T10:00:00.000Z"
  }
]
FieldDescription
roleOWNER or DEVELOPER
statusPENDING | ACTIVE | INACTIVE
invitedByUserIdOwner who sent the invite (null for Owner)
inviteExpiresAtInvitation expiry (null for non-pending)

Invite a DEVELOPER — POST /developer-portal/team/invite — Bearer, owner-only

Creates a PENDING DEVELOPER member and emails the activation link.

curl -X POST \
  "https://fin-api.digetpay.com/v1/developer-portal/team/invite" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "email": "[email protected]",
    "fullName": "Ali Mohammed"
  }'
FieldTypeRequiredDescription
emailstringYesInvitee email (case-insensitive); must be unused in the portal
fullNamestringYesInvitee display name

Response 200

{
  "ok": true,
  "developerUserId": "11",
  "role": "DEVELOPER",
  "status": "PENDING",
  "inviteExpiresAt": "2026-08-29T10:00:00.000Z",
  "emailSent": true
}
  • Re-inviting an existing PENDING member refreshes the invitation (same
    response) instead of creating a duplicate.
  • If email delivery fails, the created member is rolled back and the request
    fails.

Resend an invitation — POST /developer-portal/team/:userId/resend-invite — Bearer, owner-only

Refreshes the invitation expiry and sends a new activation link.

curl -X POST \
  "https://fin-api.digetpay.com/v1/developer-portal/team/11/resend-invite" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Response 200

{ "ok": true, "inviteExpiresAt": "2026-08-29T11:00:00.000Z", "emailSent": true }

Remove a member — DELETE /developer-portal/team/:userId — Bearer, owner-only

PENDING accounts are deleted outright; ACTIVE/INACTIVE accounts are
deactivated and their sessions revoked. The OWNER can never be removed.

curl -X DELETE \
  "https://fin-api.digetpay.com/v1/developer-portal/team/11" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Response 200 — { "ok": true }

Activate an invited account — POST /developer-portal/auth/activate — public

Completes the invite: the invitee clicks the activation link, which opens the
activate page; the page reads the token from the URL and calls this endpoint to
set the password and activate the account.

curl -X POST \
  "https://fin-api.digetpay.com/v1/developer-portal/auth/activate" \
  -H "Content-Type: application/json" \
  --data '{
    "token": "a1b2c3d4e5f6a7b8",
    "newPassword": "secure-pass-2026"
  }'
FieldTypeDescription
tokenstringActivation token from the invite email link (challenge ID)
newPasswordstring8–128 chars, at least one letter and one digit (stored with Argon2)

Response 200 — { "ok": true }

  • The activation link expires after 72 hours; the OWNER must resend after
    that.
  • After activation, future password resets go through forgot-password — the
    activation token works only once.

9.5 Team business rules

RuleBehavior
OwnershipOnly the OWNER can invite, resend, remove, or list members
Role immutabilityInvites always create a DEVELOPER; never an OWNER
Email uniquenessAn invite email must not already belong to any portal user/org
Invite expiry72 hours after sending or resending
Owner protectionThe OWNER account can never be removed
Delivery failureIf the invite email cannot be sent, the member is rolled back
First loginA member must activate (set password) before they can log in

9.6 Team errors

StatusCode / scenarioClient action
400Not the OWNERUse the OWNER account
400Invalid body, weak password, or mail not configuredCorrect the request
400Invalid or expired activation linkAsk the owner to resend a fresh link
404:userId not found in this organizationVerify the member ID
409EMAIL_IN_USE / ALREADY_A_MEMBER on inviteResolve the conflict
409Activate: account already activatedUse the forgot-password flow instead

9.7 Security considerations

  • Removing a member revokes their access immediately.
  • The activation token is an opaque random string (not a JWT), exposed only
    through the invite email link and never returned in an API response.
  • Team email addresses are company-confidential; treat the GET /team response
    accordingly.

10. Summary

  • Public endpoints cover register → OTP → login → OTP → tokens, plus
    activate for invited DEVELOPER members.

    Authenticated calls use Authorization: Bearer with a
    developer-portal-access JWT.

  • Refresh tokens rotate; logout revokes.

  • Team management and role capabilities live in §9.

You can now obtain and manage a developer session and team. Next — the full
integration flow from onboarding to live.


Did this page help you?