API reference

Authentication

Product routes use X-API-Key. Billing, team, and API-key management use a dashboard login. POST /api/auth/token does not turn an API key into that login.

Authorize product calls with X-API-Key. The key only has the scopes you grant. Billing, team, and API-key management stay in the signed-in dashboard.

Sign up (dashboard or API)

Registration is multi-step so we can verify the owner’s mobile and email before the workspace is fully active. The hosted signup page runs this for you. If you call the API directly, follow the same order.

  • GET /api/auth/captcha — receive captchaId and an image. A person types the characters. The answer is not in the JSON. Server apps skip this and use an API key.
  • POST /api/auth/register — email, password, confirmPassword, name, tenantName, mobile, captchaId, captchaAnswer. Returns a registrationToken, not a full login.
  • POST /api/auth/mobile/send-otp then /mobile/verify-otp with that token.
  • POST /api/auth/email/send-verification-code then /email/verify.
  • POST /api/auth/login (or login OTP) to receive accessToken and refreshToken.

A 14-day trial starts with the workspace. When the trial ends, the workspace moves onto the Free plan with limited quotas — data is kept. Upgrade anytime from Billing.

API key, not login

Send X-API-Key for sessions, messages, inbox, contacts, campaigns, webhooks, usage, number check, and /api/external. The key only has the scopes you grant. Billing, team, and creating keys stay in the dashboard. Never put a secret key in a public browser.

POST/api/auth/token

API key session exchange

Rejected. An API key is not exchanged for a dashboard access token. Use X-API-Key on product routes.

URL: https://api.actiwapi.com/api/auth/token

Auth: API Key

Headers

HeaderValueRequired
X-API-Key{apiKey}Yes
Content-Typeapplication/jsonYes*

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/token" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: {apiKey}"

Response example403

{
  "success": false,
  "message": "API keys cannot be exchanged for a dashboard session."
}

Try in Swagger UI

GET/api/auth/captcha

Get captcha

Human login challenge. Returns captchaId and an SVG image. The characters are not in the response. Server integrations use X-API-Key and do not call this endpoint.

URL: https://api.actiwapi.com/api/auth/captcha

Auth: None

Code examples

curl -X GET "https://api.actiwapi.com/api/auth/captcha" \
  -H "Content-Type: application/json"

Response example200

{
  "success": true,
  "data": { "captchaId": "uuid", "image": "data:image/svg+xml;base64,...", "expiresInSeconds": 300 }
}

Try in Swagger UI

POST/api/auth/register

Register

Start signup. Returns a registrationToken used for mobile and email verification. Does not issue dashboard JWTs yet.

URL: https://api.actiwapi.com/api/auth/register

Auth: None

Request example

{
  "name": "Jane Doe",
  "email": "owner@acme.com",
  "password": "SecurePass123!",
  "confirmPassword": "SecurePass123!",
  "tenantName": "Acme Corp",
  "mobile": "919876543210",
  "captchaId": "uuid",
  "captchaAnswer": "CBq32X"
}

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/register" \
  -H "Content-Type: application/json"
  -d '{  "name": "Jane Doe",  "email": "owner@acme.com",  "password": "SecurePass123!",  "confirmPassword": "SecurePass123!",  "tenantName": "Acme Corp",  "mobile": "919876543210",  "captchaId": "uuid",  "captchaAnswer": "CBq32X"}'

Response example201

{
  "success": true,
  "data": {
    "requiresVerification": true,
    "registrationToken": "jwt-registration-token",
    "nextStep": "mobile",
    "phoneMasked": "+91******3210",
    "emailMasked": "o***@acme.com"
  }
}

Try in Swagger UI

POST/api/auth/login

Login

Dashboard sign-in. Returns tokens for the website session. API integrations do not call this; they send X-API-Key.

URL: https://api.actiwapi.com/api/auth/login

Auth: None

Request example

{
  "email": "owner@acme.com",
  "password": "SecurePass123!",
  "captchaId": "uuid",
  "captchaAnswer": "CBq32X"
}

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/login" \
  -H "Content-Type: application/json"
  -d '{  "email": "owner@acme.com",  "password": "SecurePass123!",  "captchaId": "uuid",  "captchaAnswer": "CBq32X"}'

Response example200

{
  "success": true,
  "data": {
    "accessToken": "eyJhbG...",
    "refreshToken": "eyJhbG...",
    "expiresIn": "7d",
    "user": { "id": "uuid", "email": "owner@acme.com", "role": "owner" }
  }
}

Try in Swagger UI

POST/api/auth/refresh

Refresh token

Exchange a valid refresh token for a new access token.

URL: https://api.actiwapi.com/api/auth/refresh

Auth: None

Request example

{ "refreshToken": "eyJhbG..." }

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/refresh" \
  -H "Content-Type: application/json"
  -d '{ "refreshToken": "eyJhbG..." }'

Response example200

{
  "success": true,
  "data": {
    "accessToken": "eyJhbG...",
    "refreshToken": "eyJhbG...",
    "expiresIn": "7d"
  }
}

Try in Swagger UI

POST/api/auth/logout

Logout

Invalidate the refresh token so it cannot mint new access tokens.

URL: https://api.actiwapi.com/api/auth/logout

Auth: None

Request example

{ "refreshToken": "eyJhbG..." }

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/logout" \
  -H "Content-Type: application/json"
  -d '{ "refreshToken": "eyJhbG..." }'

Response example200

{ "success": true, "message": "Logged out" }

Try in Swagger UI

POST/api/auth/forgot-password

Forgot password

Send a password-reset email if the address exists. Always returns success to avoid account enumeration.

URL: https://api.actiwapi.com/api/auth/forgot-password

Auth: None

Request example

{ "email": "owner@acme.com" }

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/forgot-password" \
  -H "Content-Type: application/json"
  -d '{ "email": "owner@acme.com" }'

Response example200

{ "success": true, "message": "If the email exists, a reset link was sent" }

Try in Swagger UI

POST/api/auth/reset-password

Reset password

Set a new password using the token from the reset email.

URL: https://api.actiwapi.com/api/auth/reset-password

Auth: None

Request example

{ "token": "hex-token", "password": "NewSecurePass123!" }

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/reset-password" \
  -H "Content-Type: application/json"
  -d '{ "token": "hex-token", "password": "NewSecurePass123!" }'

Response example200

{ "success": true, "message": "Password updated" }

Try in Swagger UI

GET/api/auth/profile

Get profile

Returns the authenticated user and account context.

URL: https://api.actiwapi.com/api/auth/profile

Auth: JWT Bearer

Headers

HeaderValueRequired
AuthorizationBearer {accessToken}Yes
Content-Typeapplication/jsonYes*

Code examples

curl -X GET "https://api.actiwapi.com/api/auth/profile" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {accessToken}"

Response example200

{
  "success": true,
  "data": {
    "id": "uuid",
    "email": "owner@acme.com",
    "name": "Jane Doe",
    "role": "owner",
    "tenantId": "uuid",
    "tenantName": "Acme Corp"
  }
}

Try in Swagger UI

PUT/api/auth/profile

Update profile

Change display name or other profile fields for the current user.

URL: https://api.actiwapi.com/api/auth/profile

Auth: JWT Bearer

Headers

HeaderValueRequired
AuthorizationBearer {accessToken}Yes
Content-Typeapplication/jsonYes*

Request example

{ "name": "Jane Doe" }

Code examples

curl -X PUT "https://api.actiwapi.com/api/auth/profile" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {accessToken}"
  -d '{ "name": "Jane Doe" }'

Response example200

{ "success": true, "data": { "id": "uuid", "name": "Jane Doe" } }

Try in Swagger UI

POST/api/auth/change-password

Change password

Change password while logged in. Requires the current password.

URL: https://api.actiwapi.com/api/auth/change-password

Auth: JWT Bearer

Headers

HeaderValueRequired
AuthorizationBearer {accessToken}Yes
Content-Typeapplication/jsonYes*

Request example

{
  "currentPassword": "SecurePass123!",
  "newPassword": "EvenMoreSecure123!"
}

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/change-password" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {accessToken}"
  -d '{  "currentPassword": "SecurePass123!",  "newPassword": "EvenMoreSecure123!"}'

Response example200

{ "success": true, "message": "Password changed" }

Try in Swagger UI

POST/api/auth/login/send-otp

Resend login OTP

Resend the mobile OTP for an in-progress login challenge (after password login requires a second factor).

URL: https://api.actiwapi.com/api/auth/login/send-otp

Auth: None

Request example

{ "loginChallengeToken": "token-from-login" }

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/login/send-otp" \
  -H "Content-Type: application/json"
  -d '{ "loginChallengeToken": "token-from-login" }'

Response example200

{ "success": true, "data": { "phoneMasked": "+91******3210" } }

Try in Swagger UI

POST/api/auth/login/verify-otp

Verify login OTP

Complete a login challenge with the SMS/WhatsApp OTP (or Firebase ID token).

URL: https://api.actiwapi.com/api/auth/login/verify-otp

Auth: None

Request example

{
  "loginChallengeToken": "token-from-login",
  "otpCode": "123456"
}

Code examples

curl -X POST "https://api.actiwapi.com/api/auth/login/verify-otp" \
  -H "Content-Type: application/json"
  -d '{  "loginChallengeToken": "token-from-login",  "otpCode": "123456"}'

Response example200

{
  "success": true,
  "data": { "accessToken": "eyJhbG...", "refreshToken": "eyJhbG...", "expiresIn": "7d" }
}

Try in Swagger UI

Error codes

Failed requests return a JSON envelope with success: false and a human-readable message.

{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "phone": "Valid phone number is required"
  }
}
HTTPCodeDescription
400VALIDATION_ERRORRequest body or query failed validation.
401UNAUTHORIZEDMissing or invalid JWT / API key.
403FORBIDDENAuthenticated but lacking permission or entitlement.
403SUBSCRIPTION_INACTIVEAction not allowed on the current plan (including Free after trial). Upgrade or wait for entitlements.
403LIMIT_EXCEEDEDPlan limit reached (sessions, messages, API requests, etc.).
404NOT_FOUNDResource does not exist or is not in your account.
409CONFLICTDuplicate resource or invalid state transition.
429RATE_LIMITEDToo many requests; retry after backoff.
500INTERNAL_ERRORUnexpected server error.
502WHATSAPP_UNAVAILABLEWhatsApp session disconnected or provider error.