Skip to main content

Authentication

API v2 uses JWT tokens for authentication. Users authenticate via magic link (email code) or social login, and receive a JWT token that must be included in all subsequent requests.


Two-step flow: request a code via email, then validate it to receive a JWT.

Step 1: Request Code

POST /api/v2/login/token/init

Sends a temporary login code to the user's email. Creates the user account if it does not exist for the given tenant.

Request:

curl -X POST https://app.publica.la/api/v2/login/token/init \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"email": "user@example.com"
}'
ParameterTypeRequiredDescription
emailstringYesUser email address

Optional Headers:

HeaderDescription
X-CustomFenice-Tenant-IdTenant ID (defaults to Publica.la)
X-Fenice-LangEmail language: en, es, pt, it (defaults to en)

Response: 201 Created with empty body.

Step 2: Validate Code

POST /api/v2/login/token/validate

Validates the code received via email and returns a JWT token.

Request:

curl -X POST https://app.publica.la/api/v2/login/token/validate \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"email": "user@example.com",
"code": "12345678"
}'
ParameterTypeRequiredDescription
emailstringYesSame email used in init
codestringYesCode from email (8 characters)

Response: 200 OK

{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "k4q6r8s0t2u4v6w8x0y2z4a6b8c0d2e4f6g8h0i2j4k6l8m0n2o4p6q8r0s2t4u6"
}
FieldTypeDescription
access_tokenstringJWT to send in Authorization: Bearer ... headers
token_typestringAlways bearer
expires_inintegerAccess token TTL in seconds
refresh_tokenstring64-character opaque token used to rotate the access token. See Refresh Token below
info

The code length is configured per environment. Production uses 8 characters.


Social Login

Single-step authentication via OAuth providers.

POST /api/v2/login/social/{provider}

Supported providers: google, facebook, apple.

Request:

curl -X POST https://app.publica.la/api/v2/login/social/google \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"access_token": "ya29.a0AfH6SM..."
}'
ParameterTypeRequiredDescription
access_tokenstringYesOAuth token from the provider
warning

For Apple, the client receives both id_token and access_token. Send the id_token as the access_token parameter.

Response: 200 OK - Same JWT response as magic link validation, including refresh_token.

Error Responses:

CodeMeaning
400Invalid provider or bad request
401User declined authorization on provider
422Email not available from social provider

Refresh Token

POST /api/v2/auth/refresh

Rotates an expired or about-to-expire access token without forcing the user to re-authenticate. The previous refresh token is invalidated and a brand new JWT plus refresh token are returned.

Authentication: None. The refresh_token itself is the credential. Do not send Authorization: Bearer ....

Rate limit: 10 requests per minute per IP.

Request:

curl -X POST https://app.publica.la/api/v2/auth/refresh \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-CustomFenice-Tenant-Id: 1" \
-d '{
"refresh_token": "k4q6r8s0t2u4v6w8x0y2z4a6b8c0d2e4f6g8h0i2j4k6l8m0n2o4p6q8r0s2t4u6"
}'
ParameterTypeRequiredDescription
refresh_tokenstringYesThe opaque token returned by login (or by a previous refresh)

Optional Headers:

HeaderDescription
X-CustomFenice-Tenant-IdCustom app tenant ID (defaults to Publica.la, ID 1)

Response: 200 OK

{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "n8o0p2q4r6s8t0u2v4w6x8y0z2a4b6c8d0e2f4g6h8i0j2k4l6m8n0o2p4q6r8s0"
}

The new refresh_token replaces the old one. Clients must store and use the new value going forward; the old refresh token is no longer valid after a successful rotation. Concurrent refresh requests with the same token are safe: only one rotation succeeds.

Error Responses

CodeCause
401Refresh token invalid, expired, or already rotated
422refresh_token field missing
429Rate limit exceeded (10 requests per minute per IP)

Session Validation

GET /api/v2/auth/session/validate

Confirms that the current JWT session is still valid and within concurrency limits. This endpoint does not perform business logic; it relies entirely on the middleware stack.

Request:

curl https://app.publica.la/api/v2/auth/session/validate \
-H "Authorization: Bearer {access_token}" \
-H "Accept: application/json"

Response: 200 OK

{
"CODE": "success",
"message": "Session is valid"
}
CodeMeaning
200Session active and within limits
401Invalid or expired session
409Session limit reached for this user

Logout

GET /api/v2/logout

Invalidates the mobile session across all devices using this JWT.

Request:

curl https://app.publica.la/api/v2/logout \
-H "Authorization: Bearer {access_token}" \
-H "Accept: application/json"

Response: 200 OK with empty body.

Deletes all mobile sessions matching the JWT's jti claim.


Using the Token

Include the JWT in all authenticated requests:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

The token contains:

  • email - User email
  • jti - Unique token ID (used for session tracking)
  • exp - Expiration timestamp

See Also

X

Graph View