# Create authentication token

Universal token creation with consistent token-in-body pattern.
## Authentication Flows
### USER AUTHENTICATION
- **Header**: `x-project-key` (tenant identification)
- **Body**: `{ authToken: "jwt_from_provider", authType: "user" }`
- **Returns**: User session with PERS JWT tokens

### BUSINESS AUTHENTICATION
- **Header**: `x-project-key` (tenant identification)
- **Body**: `{ authToken: "jwt_from_provider", authType: "business", context: { businessId: "uuid" } }`
- **Returns**: Business session with PERS JWT tokens (includes `role` in JWT)
- If user has single business membership, `context.businessId` is auto-selected
- If user has multiple memberships without `context.businessId`, returns `MULTIPLE_CONTEXT_SELECTION_REQUIRED` error with available options

### ADMIN AUTHENTICATION
- **Header**: `x-project-key` (tenant identification)
- **Body**: `{ authToken: "firebase_jwt", authType: "tenant" }`
- **Returns**: Admin session with PERS JWT tokens



---

## Third-Party JWT Requirements
When providing `authToken` from an external identity provider, the JWT must meet these requirements:
### JWT Header
| Field | Required | Description |
|  --- | --- | --- |
| `kid` | Yes | Key ID matching a key in your JWKS |
| `alg` | Yes | Signing algorithm (RS256, ES256, EdDSA) |

### JWT Payload Claims
| Claim | Required | Description |
|  --- | --- | --- |
| `iss` | Yes | Issuer URL (used for JWKS discovery) |
| `sub` | Yes | Subject identifier (user ID) |
| `aud` | Yes | Audience (your registered client ID) |
| `exp` | Yes | Expiration timestamp (Unix epoch) |
| `iat` | Yes | Issued at timestamp (Unix epoch) |

### Provider Requirements
Your identity provider must expose public keys via **one of**:
1. `{issuer}/.well-known/jwks.json` (JWKS endpoint)
2. `{issuer}/.well-known/openid-configuration` (OIDC discovery with `jwks_uri`)

### Verification Flow
1. PERS extracts `iss` claim from JWT
2. Fetches JWKS from `{issuer}/.well-known/jwks.json`
3. Matches `kid` header to find signing public key
4. Verifies signature using matched key
5. Validates `exp`, `iat`, `aud` claims
6. Issues PERS access/refresh tokens on success

### PERS JWKS (For verifying our tokens)
- JWKS: `https://api.pers.ninja/.well-known/jwks.json`
- Discovery: `https://api.pers.ninja/.well-known/openid-configuration`

Endpoint: POST /auth/token
Version: 2.0.53
Security: projectKey

## Request fields (application/json):

  - `authToken` (string)
    Authentication token from identity provider (WebAuthn, DFNS, Firebase, Auth0, etc.).
**Third-Party JWT Requirements:**
- **Header**: `kid` (Key ID matching JWKS), `alg` (RS256/ES256/EdDSA)
- **Payload Claims**: `iss` (issuer URL), `sub` (user ID), `aud` (audience), `exp`, `iat`
- **Provider Requirement**: Must expose JWKS at `{issuer}/.well-known/jwks.json`

PERS will automatically discover and fetch the issuer's public keys for verification.
    Example: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xIn0.eyJpc3MiOiJodHRwczovL2lkcC5leGFtcGxlLmNvbSIsInN1YiI6InVzZXItMTIzIiwiYXVkIjoieW91ci1jbGllbnQtaWQiLCJleHAiOjE3MzM0MDAwMDAsImlhdCI6MTczMzM5NjQwMH0.…

  - `authType` (string)
    Authentication context type - explicitly declares intent for admin vs user auth
    Enum: "user", "business", "tenant", "system", "external"

  - `context` (object)
    Optional context for explicit business/tenant selection. Required when user has multiple memberships of that type.

  - `context.businessId` (string)
    Explicit business ID for multi-business users. Required when user has multiple business memberships.
    Example: 550e8400-e29b-41d4-a716-446655440000

  - `context.tenantId` (string)
    Explicit tenant ID for multi-tenant users. Required when user has multiple tenant memberships.
    Example: 550e8400-e29b-41d4-a716-446655440001

  - `rawLoginData` (object)
    if Tenant settings allowJWTForUnauthenticatedUsers is true, this field contains data to create user and setup unauthenticated user session instead of token verification. Useful for sandbox or trial modes. For now it is only used to create unauthenticated users.
    Example: {"externalId":"","email":"user@example.com","firstName":"","lastName":"","customData":{}}

## Response 200 fields (application/json):

  - `refreshToken` (string, required)
    refresh token
    Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRJZCI6IjEiLCJpYXQiOjE2MjYxNzUwNzksImV4cCI6MTYyNjE3NTI3OX0.5w8A6t7b5lBx8D3FmLwJQgJ9z1D9c9Vz7A3f7BvzH2A

  - `accessToken` (string, required)
    JWT access token this token is used to authenticate the user, by default it expires in 15 minutes, and it should be sent in the Authorization header as Bearer token.

## Response 201 fields (application/json):

  - `refreshToken` (string, required)
    refresh token
    Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRJZCI6IjEiLCJpYXQiOjE2MjYxNzUwNzksImV4cCI6MTYyNjE3NTI3OX0.5w8A6t7b5lBx8D3FmLwJQgJ9z1D9c9Vz7A3f7BvzH2A

  - `accessToken` (string, required)
    JWT access token this token is used to authenticate the user, by default it expires in 15 minutes, and it should be sent in the Authorization header as Bearer token.

  - `admin` (object)
    Admin (present if admin authentication)

  - `admin.id` (string, required)
    Id of the admin

  - `admin.email` (string, required)
    Email of the admin

  - `admin.displayName` (string, required)
    Display name of the admin

  - `admin.type` (string, required)
    Type of the admin
    Enum: "SUPER_ADMIN", "TENANT_ADMIN"

  - `admin.tenants` (array)
    Tenant of the admin

  - `admin.tenants.projectName` (string, required)
    Name of the tenant.

  - `admin.tenants.acronym` (string, required)
    Acronym of the tenant.

  - `admin.tenants.id` (string, required)
    Unique identifier for tenant. This id is used to identify tenant in the system for client requests.

  - `admin.tenants.createdAt` (string, required)
    Date when tenant was created.

  - `admin.tenants.projectDescription` (string)
    Description of the tenant.

  - `admin.tenants.projectWebsite` (string)
    Website of the tenant.

  - `admin.tenants.projectEmail` (string)
    Email of the tenant.

  - `admin.tenants.projectImageUrl` (string)
    Image URL of the tenant.

  - `admin.tenants.projectLogoUrl` (string)
    Logo URL of the tenant.

  - `admin.tenants.projectApiKey` (string, required)
    Unique identifier for tenant. This id is used to identify tenant in the system for client requests.

  - `admin.tenants.testnetProjectApiKey` (string, required)
    Testnet project API key for tenant. Used in case of testnet project.

  - `admin.tenants.stripeApiKey` (string)
    Stripe API key for tenant (masked). Used in case of stripe payment.
    Example: sk_...a1b2

  - `admin.tenants.stripeWebhookSecret` (string)
    Stripe webhook secret for tenant (masked). Used in case of stripe payment.
    Example: whsec_...x9y0

  - `admin.tenants.loginToken` (string)
    This is an optional field for any extra login requirements for your users.

  - `admin.tenants.allowJWTForUnauthenticatedUsers` (boolean, required)
    This is an optional field for any extra login requirements for your users.

  - `admin.tenants.firebaseTenantId` (string)

  - `admin.tenants.defaultWalletManagementType` (string)
    Default wallet management type for this tenant. When set, overrides chain-type-based derivation. Fallback chain: explicit → tenant default → chain type.
    Enum: "custodial", "non-custodial"

  - `admin.tenants.ipfsGatewayDomain` (string, required)
    IPFS Gateway Domain for the tenant. Used for resolving IPFS URIs to HTTP URLs.
    Example: pers.mypinata.cloud

  - `admin.tenants.googleApiKey` (string)
    Google API Key for the tenant. Used for Google services integration.

  - `admin.tenants.allowedTokenIssuers` (array)
    List of allowed external JWT token issuers for this tenant.

  - `admin.tenants.allowedTokenIssuers.issuer` (string, required)
    Issuer URL (must match token iss claim)
    Example: https://login.example.com/tenant-id/v2.0/

  - `admin.tenants.allowedTokenIssuers.publicKey` (string)
    PEM-encoded public key (skip JWKS fetch)
    Example: -----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

  - `admin.tenants.allowedTokenIssuers.jwksUri` (string)
    Custom JWKS URI (override default discovery)
    Example: https://login.example.com/.well-known/jwks.json

  - `admin.tenants.allowedTokenIssuers.allowUnverified` (boolean)
    Skip signature verification - use only for dev/testing

  - `admin.tenants.allowedTokenIssuers.allowExpired` (boolean)
    Allow expired tokens - use only for dev/testing

  - `admin.tenants.emailConfig` (object)
    Unified email configuration - provider, credentials, sender info, and templates

  - `admin.tenants.emailConfig.provider` (string)
    Email provider (sendgrid, mailjet)
    Enum: "sendgrid", "mailjet"

  - `admin.tenants.emailConfig.fromEmail` (string)
    Sender email address (must be verified with provider)
    Example: noreply@tenant.com

  - `admin.tenants.emailConfig.fromName` (string)
    Sender display name
    Example: Tenant Name

  - `admin.tenants.emailConfig.templates` (object)
    Template configurations keyed by email type

  - `admin.tenants.emailConfig.templates.magicLink` (object)
    Magic link / passwordless login email template

  - `admin.tenants.emailConfig.templates.magicLink.templateId` (string, required)
    Provider-specific template ID (e.g., SendGrid dynamic template ID)
    Example: d-abc123def456

  - `admin.tenants.emailConfig.templates.magicLink.subject` (string)
    Optional subject - if not provided, template's default subject is used
    Example: Your login link

  - `admin.tenants.emailConfig.templates.magicLink.variableMapping` (object)
    Maps provider template variable names to internal variable names. Format: { templateVar: internalVar }. Allows multiple template vars to reference the same internal value.
    Example: {"link_url":"magicLinkUrl","offers_link":"magicLinkUrl"}

  - `admin.tenants.emailConfig.templates.welcome` (object)
    Welcome email template for new users

  - `admin.tenants.emailConfig.templates.passwordReset` (object)
    Password reset email template

  - `admin.tenants.emailConfig.templates.emailVerification` (object)
    Email verification template

  - `admin.tenants.emailConfig.templates.transactionConfirmation` (object)
    Transaction confirmation template

  - `admin.tenants.emailConfig.apiKey` (string)
    Email provider API key (masked in responses)
    Example: ...a1b2

  - `admin.tenants.emailConfig.apiSecret` (string)
    Email provider API secret for Mailjet (masked in responses)
    Example: ...x9y8

  - `admin.tenants.approvalSettings` (object)
    Per-entity approval requirements. When true, newly created entities of that type will start in pending_approval state and must be approved by a tenant admin before becoming active.
    Example: {"business":false,"tokenMetadata":false,"campaign":false,"redemption":false}

  - `user` (object)
    User (present if user authentication)

  - `user.id` (string, required)

  - `user.email` (string)

  - `user.identifierEmail` (string, required)
    Universal identifier email for deterministic operations. Generated from B2B inputs (email, externalId) for wallet salt generation and external integrations.
    Example: user123@user.pers.internal

  - `user.firstName` (string, required)
    User first name

  - `user.lastName` (string, required)
    User last name

  - `user.externalId` (string, required)
    User external id

  - `user.accountAddress` (string, required)
    User account address

  - `user.instagramAccountId` (string, required)
    Instagram account id

  - `user.googleAccountName` (string, required)
    Google account name

  - `user.customData` (object, required)
    Custom data

  - `user.publicProfile` (object, required)
    Public profile data

  - `user.isActive` (boolean, required)
    Is active

  - `user.currentSigningAccountId` (string)
    Current active signing account ID for external wallet operations

  - `user.wallets` (array, required)
    User-owned counterfactual smart contract wallets that can receive tokens

  - `user.wallets.id` (string, required)
    Unique identifier for the internal wallet

  - `user.wallets.ownerType` (string, required)
    Owner type for polymorphic ownership
    Enum: "user", "business", "tenant", "system", "external"

  - `user.wallets.ownerId` (string, required)
    Owner ID for polymorphic ownership
    Example: user_123

  - `user.wallets.walletManagementType` (string, required)
    Type of internal wallet
    Enum: "custodial", "non-custodial"

  - `user.wallets.address` (string, required)
    CREATE2 generated address that can receive tokens

  - `user.wallets.chainId` (number, required)
    Blockchain network chain identifier

  - `user.wallets.status` (string, required)
    Current status of the wallet
    Enum: "pending", "active", "suspended", "archived"

  - `user.wallets.ownerSigningAccountId` (string)
    ID of signing account that owns this internal wallet

  - `user.wallets.tags` (array, required)
    Tags associated with the wallet for categorization

  - `user.wallets.createdAt` (string, required)
    Timestamp when the wallet was created

  - `user.wallets.updatedAt` (string, required)
    Timestamp when the wallet was last updated

  - `user.createdAt` (string, required)
    Timestamp when the user was created

  - `user.updatedAt` (string, required)
    Timestamp when the user was last updated

  - `user.registrationSource` (object)
    Registration source tracking for analytics - captures channel and attribution when user was created
    Example: {"channel":"web","medium":"referral","campaign":"launch_2026"}

  - `user.lastActivityAt` (string)
    Last activity timestamp. Updated whenever user generates tokens (login or refresh). Tracks last time user was active (~1 hour precision).

  - `user.activityCount` (number)
    Total activity count. Increments on every token generation (login + refresh). Measures true user engagement.
    Example: 42

  - `user.included` (object)
    Included related entities. Only populated when include parameter is specified.

  - `user.included.statusTypes` (array)
    User status types earned based on token balances (via ?include=status)

  - `user.included.statusTypes.name` (string, required)
    User Status Type name

  - `user.included.statusTypes.description` (string)
    User Status Type description

  - `user.included.statusTypes.minTokenBalance` (string, required)
    User Status Type eligible Token Addresses

  - `user.included.statusTypes.discountPercentage` (number, required)
    User Status Type discount Rate in percentage

  - `user.included.statusTypes.imageUrl` (string)
    User Status Type image Url

  - `user.included.statusTypes.eligibleTokenAddresses` (array)
    Eligible token contract addresses for this status type
    Example: ["0x1234...","0x5678..."]

  - `user.included.statusTypes.tags` (array)
    Tags for categorization and filtering
    Example: ["vip","premium","gold"]

  - `user.included.statusTypes.order` (number)
    Explicit ordering for status hierarchy (higher = more prestigious). If not set, falls back to minTokenBalance for ordering.
    Example: 100

  - `user.included.statusTypes.id` (number, required)
    User Status Type id

  - `user.included.tokenBalances` (array)
    Token balances for user wallets (via ?include=balances)

  - `user.included.tokenBalances.accountAddress` (string, required)

  - `user.included.tokenBalances.tokenBalances` (array, required)

  - `user.included.tokenBalances.tokenBalances.contractAddress` (string, required)

  - `user.included.tokenBalances.tokenBalances.chainId` (number, required)

  - `user.included.tokenBalances.tokenBalances.balance` (number, required)

  - `user.included.tokenBalances.tokenBalances.tokenName` (string)

  - `user.included.tokenBalances.tokenBalances.tokenSymbol` (string)

  - `user.included.tokenBalances.tokenBalances.tokenType` (string, required)

  - `user.included.tokenBalances.tokenBalances.tokenId` (string, required)

  - `user.included.tokenBalances.tokenBalances.metadataUri` (string, required)

  - `user.included.tokenBalances.tokenBalances.metadata` (object, required)
    Token metadata (loaded from IPFS/storage when needed for filtering)

  - `business` (object)
    Business (present if business authentication)

  - `business.id` (string, required)
    The id of the business, this is unique and will be used to identify the business.

  - `business.email` (string, required)
    The email of the business, this is unique and will be used to identify the business.

  - `business.accountAddress` (string, required)
    The address of the business, this is the address that will be shown to the public.

  - `business.currentSigningAccountId` (string, required)
    Current active signing account ID for external wallet operations

  - `business.wallets` (array, required)
    Business-owned counterfactual smart contract wallets that can receive tokens

  - `business.businessLegalName` (string, required)
    The legal name of the business, this is the name that will be used for legal purposes.

  - `business.registrationNumber` (string, required)
    The business registration number (e.g., company registration, VAT number, EIN)

  - `business.displayName` (string, required)
    The display name of the business, this is the name that will be shown to the public.

  - `business.description` (string, required)
    The description of the business, this is the description that will be shown to the public.

  - `business.shortDescription` (string, required)
    The short description of the business, this is the description that will be shown to the public.

  - `business.websiteUrl` (string, required)
    The website of the business, this is the website that will be shown to the public.

  - `business.imageUrl` (string, required)
    The image of the business, this is the image that will be shown to the public.

  - `business.logoUrl` (string, required)
    Logo URL for the business

  - `business.streetAddress` (string, required)
    The address of the business, this is the address that will be shown to the public.

  - `business.phoneNumber` (string, required)
    The phone number of the business.

  - `business.neighborhood` (string, required)
    Neighborhood/area name (e.g., "West Bay", "Pearl Qatar", "Lusail") - auto-populated from geocoding

  - `business.district` (string, required)
    District/administrative area - auto-populated from geocoding

  - `business.city` (string, required)
    The city of the business, this is the city that will be shown to the public.

  - `business.postalCode` (string, required)
    The postal code of the business, this is the postal code that will be shown to the public.

  - `business.country` (string, required)
    The country of the business (auto-populated from geocoding if coordinates provided)

  - `business.countryCode` (string, required)
    ISO 3166-1 alpha-2 country code (e.g., QA, US, AE) - auto-populated from geocoding
    Example: QA

  - `business.coordsLatitude` (number, required)
    The latitude of the business, this is the latitude that will be shown to the public.

  - `business.coordsLongitude` (number, required)
    The longitude of the business, this is the longitude that will be shown to the public.

  - `business.businessType` (object, required)
    The business type of the business, this is the business type that will be shown to the public.

  - `business.businessType.id` (string)
    Business type id

  - `business.businessType.name` (string, required)
    Business type name

  - `business.businessType.description` (string, required)
    Business type description

  - `business.businessType.code` (string, required)
    Business type code

  - `business.businessType.iconUrl` (string, required)
    Business type icon url

  - `business.isActive` (boolean, required)
    The status of the business, this is the status that will be shown to the public.

  - `business.approval` (object, required)
    Approval metadata for this business.

  - `business.approval.status` (string, required)
    Approval state: pending_approval = awaiting admin review, approved = active, rejected = denied.
    Enum: "pending_approval", "approved", "rejected"

  - `business.approval.approvedAt` (string)
    Timestamp when the entity was approved or rejected by an admin.

  - `business.approval.approvedBy` (string)
    Admin user ID who approved or rejected this entity.

  - `business.approval.rejectionReason` (string)
    Reason provided when the entity was rejected.

  - `business.canMintToken` (boolean, required)
    The ability to mint token for the business, this is the ability that will be shown to the public.

  - `business.canChargeToken` (boolean, required)
    The ability to charge token for the business, this is the ability that will be shown to the public.

  - `business.canManageUsers` (boolean, required)
    The ability to manage users for the business, this is the ability that will be shown to the public.

  - `business.canReceiveDonation` (boolean, required)
    The ability to receive donation for the business, this is the ability that will be shown to the public.

  - `business.tags` (array, required)
    Tags for categorization and filtering

  - `business.customData` (object, required)
    Custom business data including Google Places information (placeID, currentOpeningHours)
    Example: {"placeID":"ChIJN1t_tDeuEmsRUsoyG83frY4","currentOpeningHours":{"open_now":true,"weekday_text":["Monday: 9:00 AM – 5:00 PM","Tuesday: 9:00 AM – 5:00 PM"]}}

  - `authType` (string, required)
    Authentication context type - explicitly declares intent for admin vs user auth
    Enum: "user", "business", "tenant", "system", "external"

