Keep your access tokens safe. Never expose bearer tokens in client-side JavaScript, public repositories, or browser-bundled code. Treat them like passwords.

1. Get a token

Call POST /api/login with your email and password, or POST /api/register to create a new account. The response includes an access_token that you can use on every subsequent request.

REQUEST
curl -X POST /api/login \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "supersecret123"
  }'

The response includes a Sanctum personal-access token:

RESPONSE
{
  "access_token": "1|abc123def456...",
  "token_type": "Bearer",
  "user": { "id": 1, "name": "Jane Doe", "email": "[email protected]" }
}

2. Pass it in the Authorization header

For every authenticated request, add this header:

HEADER
Authorization: Bearer YOUR_ACCESS_TOKEN

You may also need:

COMMON HEADERS
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Content-Type: application/json

3. Full example: authenticated request

Below is a complete example of an authenticated POST request to /api/transactions.

curl -X POST /api/transactions \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": 1,
    "type": "expense",
    "amount": 25.50,
    "description": "Lunch with team",
    "transaction_date": "2026-06-03"
  }'
const response = await fetch('/api/transactions', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + token,
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    account_id: 1,
    type: 'expense',
    amount: 25.50,
    description: 'Lunch with team',
    transaction_date: '2026-06-03',
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
$ch = curl_init('/api/transactions');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer YOUR_ACCESS_TOKEN',
        'Accept: application/json',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'account_id'       => 1,
        'type'             => 'expense',
        'amount'           => 25.50,
        'description'      => 'Lunch with team',
        'transaction_date' => '2026-06-03',
    ]),
]);

$response = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new RuntimeException('Request failed: ' . $status);
}

print_r(json_decode($response, true));
import requests

response = requests.post(
    '/api/transactions',
    headers={
        'Authorization': f'Bearer {token}',
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    json={
        'account_id': 1,
        'type': 'expense',
        'amount': 25.50,
        'description': 'Lunch with team',
        'transaction_date': '2026-06-03',
    },
)
response.raise_for_status()
print(response.json())

Rotating & revoking tokens

Call POST /api/logout to revoke the token used for the current request. If a token is leaked, log in again to receive a fresh one and treat the leaked token as compromised.

Authentication errors

If your token is missing, malformed, or revoked, the API will respond with 401 Unauthorized:

401 Unauthorized
{
  "message": "Unauthenticated."
}

Email verification (opt-in)

The User model implements MustVerifyEmail. A verification email is queued automatically on register — the response includes email_verified: false until the user clicks the link.

Verification is not required to use the API. The only endpoints that return a 403 for unverified users are the sensitive bulk writers: POST /api/transactions/sync, POST /api/import/store, and POST /api/import/kuda/store. Those return { "message": "Your email address is not verified.", "requires_verified_email": true } so the frontend can prompt the user to verify.

The SPA can poll GET /api/email/verification-status to decide whether to render a “please confirm your email” banner without fetching the full profile, and call POST /api/email/verification-notification (throttled 6/min) to resend the link.

Google OAuth (GET /api/auth/google) auto-verifies the user on first login, so users who sign in with Google never see the verification banner.

Full reference on the Authentication page.

© 2026 Pasona Finance Tracker API. All rights reserved.

API version: v1