Zodiac Tech CRM Docs

Authentication

How REST API keys and mobile device tokens are created, sent, scoped, expired and revoked — with working curl examples.

developer admin super-admin Medium ⏱ 15 min Updated 2026-10-06

Zodiac Tech CRM has two independent credential systems:

  1. API keys — long-lived, per-workspace, scope-limited. For servers and no-code tools hitting /api/v1/*.
  2. Device tokens — per-user, minted by mobile login, 90-day life. For the Android app on /api/v1/mobile/*.

This page covers both end to end.

Who this is for

  • Admin — creating, scoping and revoking API keys for the workspace.
  • Developer — wiring a server integration or diagnosing 401/403 responses.
  • Super-admin — auditing which keys exist and where the limits sit.

Prerequisites: access to the admin panel at /admin with permission to manage API keys (api_keys permissions), and an install reachable at your base URL.


Part 1 — REST API keys

Step 1: Create a key

  1. Log in to the tenant admin panel (https://your-domain.com/admin).
  2. Open Settings → API Keys and click Create.
  3. Fill in:
    • Name — a label for the integration (max 100 characters), e.g. Zapier – Facebook leads.
    • Scopes — tick exactly what the integration needs (see the scope table below).
    • Rate limit per hour — default 1000, allowed range 1–100000.
    • Expires at — optional. Leave empty for a non-expiring key.
  4. Save. The raw key is shown once (lh_ + 40 random characters). Copy it immediately — it is never displayed again.

Under the hood the key is stored as a SHA-256 hash; the visible prefix (first 8 characters) identifies it later in the list. You can revoke a key at any time from the same screen, and delete it if no longer needed.

Treat the key like a password Anyone holding the key can act as your workspace within its scopes. Store it in your integration's secret store, never in client-side code or a public repository.

Step 2: Choose scopes

Each route requires a specific scope (enforced per route via the api.key:<scope> middleware). Requesting more scopes than needed is the most common mistake.

Scope Grants
read:leads / write:leads / delete:leads List & view / create & update / delete leads, lead tags
read:pipelines / write:pipelines Pipelines and their stages
read:tags / write:tags Tags
read:users / write:users / delete:users Workspace users
read:forms / write:forms / delete:forms Forms (write also covers submitting entries)
read:automations / write:automations / delete:automations Automations
read:integrations / write:integrations Integration connections (write covers create/update/delete)

Step 3: Send the key

Every REST request carries the key in the standard Bearer position:

Authorization: Bearer lh_yourKeyHere
Accept: application/json
# List the 25 most recent leads
curl -s "https://crm.zodiactechsoft.in/api/v1/leads?per_page=25" \
  -H "Authorization: Bearer lh_yourKeyHere" \
  -H "Accept: application/json"

A successful response uses the standard envelope (data + pagination meta/links):

{
  "data": [ { "id": 101, "first_name": "Aarav", "...": "..." } ],
  "meta": { "current_page": 1, "last_page": 4, "per_page": 25, "total": 92, "from": 1, "to": 25 },
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." }
}

Step 4: Handle key failure

Response Meaning
401 API key required. Provide Authorization: Bearer <key> header. Header missing/malformed
401 Invalid or expired API key. Key revoked, expired, or hash mismatch
403 Insufficient scope. Required: read:leads Key lacks the scope for that route
403 This workspace is not active. Renew or reactivate the subscription to use the API. Tenant is inactive

Subfolder installs and curl

When the CRM lives in a subfolder (for example the XAMPP dev install), the subfolder is part of the URL and must be URL-encoded or quoted:

# Local XAMPP install — note the encoded space in "Lead%20Hub"
curl -s "http://localhost/Lead%20Hub/api/v1/leads" \
  -H "Authorization: Bearer lh_yourKeyHere" \
  -H "Accept: application/json"

In a browser or tool that handles redirects (Postman, Insomnia), keep the URL exactly as above. If a tool strips the Authorization header on redirects, call the versioned /api/v1/... path directly to avoid the 308 hop.

Apache and the Authorization header The shipped root .htaccess re-exports the Authorization header into the request environment (E=HTTP_AUTHORIZATION), which is what makes Bearer auth work on Apache CGI/FastCGI setups. On a standard install (document root pointed at public/) the same rule lives in public/.htaccess.


Part 2 — Mobile device tokens

Device tokens are minted by the mobile API itself and belong to an individual user, not the workspace.

Login (password)

curl -s -X POST "https://crm.zodiactechsoft.in/api/v1/mobile/login" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "agent@example.com",
    "password": "your-password",
    "device_uuid": "a-unique-device-id"
  }'

Notes:

  • identifier accepts the user's e-mail or phone number. A legacy email field is still accepted for older app builds.
  • device_uuid is required and ties the token to a physical device (re-registering the same UUID rebinds it).
  • The route is throttled to 10 requests/minute per IP.

The response contains the token and its expiry:

{
  "data": {
    "token": "64-character-random-token",
    "expires_at": "2027-01-03T09:00:00+05:30",
    "user": { "id": 7, "name": "Priya", "email": "agent@example.com", "phone": "+91...", "role": "member" },
    "tenant": { "id": 12, "name": "Acme Realty", "slug": "acme", "timezone": "Asia/Kolkata" },
    "device": { "id": 3, "...": "..." }
  }
}

OTP login

If the workspace enables mobile OTP login:

  1. POST /api/v1/mobile/otp/start with the identifier (phone or email) sends a code. Limits: 3 per identifier and 10 per IP per 10 minutes.
  2. POST /api/v1/mobile/otp/verify with the identifier, code and device_uuid returns the same login payload as above (Firebase builds send id_token instead of code).

GET /api/v1/mobile/auth-config tells the app which methods the platform defaults support before login; per-workspace overrides resolve at login and OTP.

Using the token

curl -s "https://crm.zodiactechsoft.in/api/v1/mobile/home" \
  -H "Authorization: Bearer <device-token>" \
  -H "Accept: application/json"

DeviceAuthenticate resolves the token's user and workspace; every query is scoped to that workspace via the tenant global scope.

Token lifecycle

Event Effect
Login on the same device UUID Previous token is replaced; a fresh 90-day token is issued
Login on a new device New token + device record
POST /api/v1/mobile/logout Token is revoked (revoked_at set) immediately
Token older than 90 days Expired — the app must log in again
User or workspace deactivated Requests return 401/403; see Error codes & handling

Mobile error responses

{ "error": { "message": "Invalid email or password.", "code": "INVALID_CREDENTIALS" } }

Common codes: INVALID_CREDENTIALS (401), ACCOUNT_SUSPENDED (403), NO_WORKSPACE (403), WORKSPACE_INACTIVE (403), ACCOUNT_LOCKED (429 after 5 failed password attempts), RATE_LIMITED (429 — otp/start includes retry_after; login/OTP 429s come from the route throttle), otp_invalid (401).

Common mistakes

  • Creating a key with all scopes "just in case" — a later breach now reaches every resource. Grant the minimum.
  • Pasting the key into a browser address bar — it must be an Authorization header, never a query string.
  • Reusing one device token across several agents — tokens are per user; each phone logs in for itself.
  • Forgetting to store the raw API key at creation — it cannot be retrieved later; revoke and recreate instead.

Troubleshooting

Symptom Fix
401 on every request Check header spelling (Bearer prefix with a space) and that the key was not revoked
403 Insufficient scope Edit the key and add the missing scope
Mobile 401 after weeks of use Token expired at 90 days — log in again
Mobile NO_WORKSPACE (403) The user has no active workspace membership; fix in Super Admin → Users
Login works but lists are empty The user's workspace has no data visible to that role — not an auth problem

Technical details

  • REST auth: App\Http\Middleware\ApiKeyAuthentication — resolves tenant_id from the key record, binds current_tenant, enforces scopes and both hourly limits.
  • Key model: App\Models\ApiKey (ALL_SCOPES constant, SHA-256 lookup, lh_ prefix generation, revoke/expire flags).
  • Admin UI: App\Filament\Resources\ApiKeyResource (tenant Settings → Advanced; plan-gated by the api feature and the max_api_keys quota).
  • Mobile auth: App\Http\Controllers\Api\Mobile\AuthController (TOKEN_TTL_DAYS = 90, OTP attempt cap 5) and App\Http\Middleware\DeviceAuthenticate.

Was this page helpful?