Dokumentasi Developer

PAVLO API & Integrasi

Integrasi aplikasi pihak ketiga dengan PAVLO. Fitur premium — aktif untuk tenant dengan paket yang menyertakan API Access / SSO.

Base API: https://api.pavlo.idAplikasi: https://app.pavlo.id

Dua mekanisme integrasi

API Key (M2M)OIDC SSO
UntukServer-to-server: baca data, validasi kredensial"Login with PAVLO" — user login pakai akun PAVLO
AuthAPI key statis (pavlo_live_…)OAuth2 / OpenID Connect (redirect)
Password userApp Anda mengirim email+password userUser login langsung di PAVLO (app tak pegang password)
Fitur planapi_accesssso

Rekomendasi: untuk login user pihak ketiga gunakan OIDC — user tak menyerahkan password ke aplikasi Anda. API Key cocok untuk integrasi data server-to-server & validasi kredensial terkontrol.

A. API Key (M2M)

A.1 Membuat API Key

Admin tenant → Settings › API & Integrasi › Buat API Key. Pilih scope + (opsional) kedaluwarsa. Secret penuh (pavlo_live_…) ditampilkan sekali — simpan dengan aman.

  • auth:validate — validasi login user / ESS / klien
  • read:employees — baca daftar karyawan
  • read:payslips, read:clients — disiapkan untuk endpoint berikutnya

A.2 Autentikasi

Authorization: Bearer pavlo_live_xxxxxxxx...
# atau
X-API-Key: pavlo_live_xxxxxxxx...

Rate limit 600 request/menit per key. Key terikat ke satu tenant — semua data & validasi otomatis ter-scope.

A.3 Endpoint — https://api.pavlo.id/api/external/v1

Validasi kredensial (scope auth:validate)

POST /api/external/v1/auth/validate/user      # user admin/staff tenant
POST /api/external/v1/auth/validate/ess        # karyawan ESS (tenant = key)
POST /api/external/v1/auth/validate/client     # PIC klien

Body: { "email": "...", "password": "..." }

Response 200:

{
  "success": true,
  "data": {
    "valid": true,
    "identity": { "id":"...", "email":"...", "name":"...", "tenant_id":"...", "role":"admin" },
    "token": { "access_token":"<JWT>", "token_type":"Bearer", "expires_in":300 }
  }
}

Kredensial salah → 401. Identitas lintas-tenant ditolak (terikat ke tenant key).

Baca data (scope read:employees)

GET /api/external/v1/employees?q=&status=&page=1&per_page=50

A.4 Keamanan

  • Secret di-hash SHA-256 (tak pernah disimpan plaintext).
  • Rotate / cabut kapan saja dari Settings. Setiap panggilan di-audit.
  • ⚠️ auth:validate berarti app Anda memegang password user — untuk login user pihak ketiga gunakan OIDC.

B. OIDC SSO — “Login with PAVLO”

PAVLO bertindak sebagai OpenID Provider. Aplikasi Anda (Relying Party) mendelegasikan login via OAuth2 Authorization Code + PKCE.

  • Issuer: https://api.pavlo.id
  • Discovery: GET /.well-known/openid-configuration
  • JWKS: GET /oauth/jwks — token RS256, verifikasi tanpa secret
  • Grant authorization_code + PKCE S256 (wajib), public client (tanpa secret)
  • Scopes: openid profile email · subjek (MVP): user admin/staff tenant

B.1 Registrasi Aplikasi

Admin tenant → Settings › API & Integrasi › SSO › Daftar Aplikasi. Isi nama + Redirect URI (wajib https://, atau http://localhost untuk dev). Anda dapat client_id (pavlo_client_…).

B.2 Endpoint OIDC

https://api.pavlo.id/.well-known/openid-configurationGETDiscovery document
https://api.pavlo.id/oauth/jwksGETPublic keys (verifikasi token)
https://app.pavlo.id/oauth/authorizeGET (browser)Halaman login + consent
https://api.pavlo.id/oauth/tokenPOST (form)Tukar code → token
https://api.pavlo.id/oauth/userinfoGET (Bearer)Klaim profil user

Endpoint authorization ada di host aplikasi; sisanya di host API. Library OIDC standar membaca ini otomatis dari discovery — cukup arahkan ke issuer https://api.pavlo.id.

B.3 Klaim id_token & rotasi key

iss, sub, aud, exp, iat, nonce, email, name, tenant_id. Token berlaku 1 jam; authorization code single-use 60 detik.

PAVLO mendukung rotasi key multi-kid. RP wajib mencocokkan kid header token ke key di JWKS & re-fetch JWKS berkala (jangan hardcode satu key) — library OIDC standar menangani ini otomatis.

Guide Integrasi

Guide 1 — Validasi login via API Key (M2M)

curl -X POST https://api.pavlo.id/api/external/v1/auth/validate/ess \
  -H "Authorization: Bearer pavlo_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"email":"karyawan@perusahaan.com","password":"rahasia"}'
# → 200 { valid:true, identity:{...}, token:{ access_token, expires_in:300 } }
# → 401 jika kredensial salah

Guide 2a — OIDC dengan library standar (rekomendasi)

Arahkan client OIDC ke issuer https://api.pavlo.id. Contoh openid-client (Node.js):

import { Issuer, generators } from 'openid-client'

const pavlo = await Issuer.discover('https://api.pavlo.id')
const client = new pavlo.Client({
  client_id: 'pavlo_client_xxx',
  redirect_uris: ['https://app-anda.com/callback'],
  response_types: ['code'],
  token_endpoint_auth_method: 'none', // public client (PKCE)
})

// Mulai login:
const code_verifier = generators.codeVerifier()
const code_challenge = generators.codeChallenge(code_verifier)
const authUrl = client.authorizationUrl({
  scope: 'openid profile email',
  code_challenge, code_challenge_method: 'S256', state, nonce,
})
// simpan code_verifier+state, redirect user ke authUrl

// Di /callback:
const params = client.callbackParams(req)
const tokenSet = await client.callback(redirectUri, params, { code_verifier, state, nonce })
const claims = tokenSet.claims() // { sub, email, name, tenant_id } — terverifikasi vs JWKS

Passport, Spring Security, dll. tinggal diarahkan ke issuer yang sama.

Guide 2b — NextAuth / Auth.js v5 (Next.js)

Tambahkan PAVLO sebagai custom OIDC provider. Auto-discovery dari issuer; PKCE + public client.

// auth.ts
import NextAuth from "next-auth"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    {
      id: "pavlo",
      name: "PAVLO",
      type: "oidc",
      issuer: "https://api.pavlo.id",         // auto-discovery
      clientId: process.env.PAVLO_CLIENT_ID,  // pavlo_client_xxx
      client: { token_endpoint_auth_method: "none" }, // public client (PKCE, tanpa secret)
      checks: ["pkce", "state", "nonce"],
      authorization: { params: { scope: "openid profile email" } },
      profile(p) {
        return { id: p.sub, name: p.name, email: p.email }
      },
    },
  ],
})

// app/api/auth/[...nextauth]/route.ts
export { GET, POST } from "@/auth"

Redirect URI yang didaftarkan di PAVLO: https://app-anda.com/api/auth/callback/pavlo (pola callback NextAuth: {NEXTAUTH_URL}/api/auth/callback/{providerId}). Trigger login via signIn("pavlo").

Guide 2c — OIDC manual (PKCE, tanpa library)

# 1. Generate PKCE (S256)
code_verifier=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
code_challenge=$(printf %s "$code_verifier" | openssl dgst -binary -sha256 \
  | openssl base64 | tr '+/' '-_' | tr -d '=')

# 2. Redirect browser ke authorization endpoint:
#    https://app.pavlo.id/oauth/authorize?response_type=code
#      &client_id=pavlo_client_xxx&redirect_uri=https://app-anda.com/callback
#      &scope=openid%20profile%20email&state=<acak>
#      &code_challenge=$code_challenge&code_challenge_method=S256&nonce=<acak>
#  → user login+setuju → balik ke https://app-anda.com/callback?code=<CODE>&state=<acak>

# 3. Tukar code → token (server-to-server, form-encoded)
curl -X POST https://api.pavlo.id/oauth/token \
  -d grant_type=authorization_code -d code=<CODE> \
  -d code_verifier=$code_verifier -d client_id=pavlo_client_xxx \
  -d redirect_uri=https://app-anda.com/callback
# → { id_token, access_token, token_type:"Bearer", expires_in:3600, scope }

# 4. (opsional) profil
curl https://api.pavlo.id/oauth/userinfo -H "Authorization: Bearer <access_token>"
Checklist verifikasi id_token (wajib di RP): tanda tangan valid (RS256, key dari JWKS sesuai kid) · iss = https://api.pavlo.id · aud = client_id Anda ·exp belum lewat · nonce cocok.

Referensi Error

API eksternal (/api/external/v1/*)

  • 401 — API key tidak valid / dicabut / kedaluwarsa, atau kredensial salah
  • 403 — fitur api_access tak ada di plan, atau key tak punya scope
  • 429 — rate limit per-key terlampaui (600/menit)

OIDC token endpoint (/oauth/token)

Format { "error": "...", "error_description": "..." }:

  • invalid_grant — code salah/kedaluwarsa/terpakai, PKCE tak cocok, redirect_uri/client_id beda
  • oidc_not_configured (503) — OIDC belum diaktifkan

Semua endpoint memerlukan tenant dengan paket premium yang sesuai (api_access / sso). Butuh akses? Hubungi admin tenant Anda atau tim PAVLO.