Use this API to send transactional messages and manage marketing data from external applications such as 1app.
Production base URL: https://grow.1app.online/api/v1/
In-app guide: Sidebar Developers (/developers) or Settings → Developer API
Full API reference (public): /developers/docs — styled HTML; raw markdown at /developers/docs/raw
Postman: Collection · Environment — import into Postman, set apiToken, and run requests.
Delivery behaviour
Transactional API sends are immediate — they do not enter the campaign queue.
| Path | Behaviour |
|---|---|
POST /messages/* |
Synchronous HTTP: provider is called in the same request; response includes sent or failed. |
| Campaign broadcasts | Queued separately with rate limiting — transactional sends are not delayed by active campaigns. |
OTP and other transactional traffic from 1app will not wait behind an active WhatsApp/SMS/email campaign. For best isolation during large broadcasts, use a dedicated WhatsApp number for authentication templates.
Postman
- Download the Postman collection (optional environment).
- In Postman: Import → upload the JSON file, or Import → Link and paste the collection URL.
- Set collection variable
apiTokento your API token (read+write). - Confirm
baseUrlmatches your deployment (defaults to this server's/api/v1URL on download).
The collection includes transactional sends (WhatsApp/SMS OTP, email, SMS, templates), contacts, lists, campaigns, and lead submission.
Authentication
- Log in to 1app Grow and select the workspace that has your email/SMS/WhatsApp providers configured.
- Open Profile → API Tokens and create a token.
- Enable both permissions on the token:
write— required for sending messages and creating/updating contacts and listsread— required for listing resources and checking message delivery status
If you see "Invalid ability provided.", your token is missing write. Recreate the token and check read + write (not legacy create/update/delete labels).
Send the token on every request:
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json
The API uses the currently selected workspace on the user account that owns the token. Create separate tokens per workspace if you operate multiple tenants.
Idempotency
Transactional send endpoints accept an idempotency key to prevent duplicate sends when your client retries:
- Header:
Idempotency-Key: unique-string-per-attempt - Or JSON body field:
"idempotency_key": "unique-string-per-attempt"
If the same key is reused for the same workspace, the original message record is returned instead of sending again.
Transactional messaging
All send endpoints require the write ability and are rate-limited to 300 requests per minute per token.
WhatsApp OTP (recommended for 1app phone verification)
POST /messages/whatsapp/otp
Sends a one-time code using an AUTHENTICATION category WhatsApp template approved in Meta.
Body
| Field | Required | Description |
|---|---|---|
to |
yes | E.164 phone number, e.g. +2348012345678 |
code |
yes | OTP string, max 15 characters (Meta authentication limit) |
template |
no* | Meta template name (meta_template_name in Grow) |
language |
no | Template language code, default en |
from |
no | Sender phone number exactly as shown in Grow (WhatsApp → Numbers), e.g. +2348012345678 |
whatsapp_number_id |
no | Alternative to from — numeric ID from GET /whatsapp-numbers |
idempotency_key |
no | See Idempotency |
Use from or whatsapp_number_id, not both. If neither is set, the first active workspace number is used.
*Required unless your workspace administrator has configured a default OTP template.
Example
curl -X POST "https://grow.1app.online/api/v1/messages/whatsapp/otp" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: verify-user-9912" \
-d '{
"to": "+2348012345678",
"code": "482910",
"template": "1app_verify",
"language": "en"
}'
Prerequisites
- Active WhatsApp number on the workspace (WhatsApp → Numbers)
- Approved AUTHENTICATION template synced in Grow (WhatsApp → Templates)
WhatsApp sender number
Pass the sender as from using the phone number shown in Grow under WhatsApp → Numbers (international format, e.g. +2348012345678).
If from is not on your workspace, the API returns HTTP 422:
{
"status": "failed",
"error": "The sender phone number is not registered on this workspace."
}
Alternatively, pass whatsapp_number_id from GET /whatsapp-numbers. If neither is set, the first active number on the workspace is used.
{
"to": "+2348012345678",
"code": "482910",
"template": "1app_verify",
"from": "+2349087654321"
}
The template must be approved on that specific number.
SMS OTP
POST /messages/sms/otp
| Field | Required | Description |
|---|---|---|
to |
yes | E.164 phone number |
code |
yes | OTP string |
message |
no | Custom template; :code is replaced. Default: Your verification code is :code |
idempotency_key |
no | See Idempotency |
Example
{
"to": "+2348012345678",
"code": "482910"
}
Transactional email
POST /messages/email
| Field | Required | Description |
|---|---|---|
to |
yes | Recipient email |
subject |
yes | Email subject |
html |
yes | HTML body |
idempotency_key |
no | See Idempotency |
Free-form SMS
POST /messages/sms
| Field | Required | Description |
|---|---|---|
to |
yes | E.164 phone number |
body |
yes | Message text (max 320 chars) |
idempotency_key |
no | See Idempotency |
WhatsApp template (non-OTP)
POST /messages/whatsapp
| Field | Required | Description |
|---|---|---|
to |
yes | E.164 phone number |
template |
yes | Meta template name |
language |
no | Default en |
variables |
no | Array of template variable values in order |
from |
no | Sender phone as shown in Grow (see WhatsApp sender number) |
whatsapp_number_id |
no | Alternative sender selector by ID |
idempotency_key |
no | See Idempotency |
Message status
GET /messages/{id}
Requires read ability. Returns delivery status for a message sent in the current workspace.
Response
{
"id": 42,
"channel": "whatsapp",
"to": "+2348012345678",
"status": "sent",
"provider_message_id": "wamid.xxx",
"error": null,
"idempotency_key": "verify-user-9912",
"sent_at": "2026-06-05T12:00:00+00:00",
"created_at": "2026-06-05T11:59:58+00:00"
}
status is one of: queued, sent, failed.
Send response format
Successful send (HTTP 201):
{
"id": 42,
"channel": "whatsapp",
"to": "+2348012345678",
"status": "sent",
"provider_message_id": "wamid.xxx",
"sent_at": "2026-06-05T12:00:00+00:00"
}
Failed send (HTTP 422):
{
"id": 43,
"channel": "whatsapp",
"to": "+2348012345678",
"status": "failed",
"error": "No active WhatsApp number configured for this workspace."
}
Common errors:
| Error | Fix |
|---|---|
| No active WhatsApp number | Connect a number under WhatsApp → Numbers |
| No active SMS/email provider | Add provider under SMS or Email settings |
| WhatsApp OTP template not configured | Pass template in the request body |
| OTP code must be 15 characters or fewer | Shorten the code for authentication templates |
| No workspace associated with this token | User must belong to a workspace; switch workspace and recreate token |
Integrating from 1app
Typical phone verification flow:
- User enters phone number in 1app.
- 1app backend generates a 6-digit OTP and stores it (cache/DB) with expiry.
- 1app backend calls
POST /messages/whatsapp/otp(or/messages/sms/otp) on Grow with a uniqueIdempotency-Keyper verification session. - User enters OTP in 1app; 1app validates locally.
- Optionally poll
GET /messages/{id}if you need provider-level confirmation.
PHP (Laravel) example
use Illuminate\Support\Facades\Http;
$response = Http::withToken(config('services.grow.api_token'))
->withHeaders(['Idempotency-Key' => "otp-{$userId}-".now()->timestamp])
->post(config('services.grow.base_url').'/messages/whatsapp/otp', [
'to' => $phoneE164,
'code' => $otp,
'template' => config('services.grow.whatsapp_otp_template'),
'language' => 'en',
]);
if ($response->failed()) {
report($response->json('error'));
}
Suggested .env on 1app:
GROW_API_BASE_URL=https://grow.1app.online/api/v1
GROW_API_TOKEN=your_sanctum_token
GROW_WHATSAPP_OTP_TEMPLATE=1app_verify
Contacts & lists (CRUD)
| Endpoint | Methods | Ability |
|---|---|---|
/contacts |
GET, POST | read / write |
/contacts/{id} |
GET, PUT, DELETE | read / write |
/lists |
GET, POST | read / write |
/lists/{id} |
GET, PUT, DELETE | read / write |
/lists/{id}/contacts |
GET | read |
Campaigns (read-only)
| Endpoint | Methods | Ability |
|---|---|---|
/campaigns |
GET | read |
/campaigns/{id} |
GET | read |
/campaigns/{id}/logs |
GET | read |
Lead capture
POST /leads
Authenticated lead ingestion. Requires a valid API token.
| Field | Required | Description |
|---|---|---|
name |
yes | Contact name |
email |
no | Email address |
phone |
no | Phone number |
source |
no | api, meta_ads, or tiktok_ads |
form_slug |
no | Link submission to an existing lead form |
custom_fields |
no | Key-value object for extra data |
Errors
All /api/v1/* responses are JSON. Stack traces and internal exception details are never returned.
HTTP status codes
| HTTP | Meaning |
|---|---|
| 401 | Missing or invalid token |
| 403 | Token lacks required ability — see error and required_abilities |
| 404 | Endpoint or resource not found (wrong workspace returns 404 for scoped resources) |
| 422 | Validation failure or business/send failure — see error and optional errors |
| 429 | Rate limit exceeded — see retry_after_seconds when present |
| 500 | Unexpected server error — generic message only |
Response shapes
Authentication (401)
{ "error": "Authentication required. Provide a valid API token: Authorization: Bearer {token}" }
Missing permission (403)
{
"error": "This endpoint requires the write permission on your API token. Create a new token with read + write enabled.",
"required_abilities": ["write"]
}
No workspace on token (422)
{
"error": "No workspace associated with this token. Open Grow, switch to the correct workspace, then create a new API token."
}
Validation (422)
{
"error": "The given data was invalid.",
"errors": { "name": ["The name field is required."] }
}
Send failure (422) — transactional endpoints
{
"id": 43,
"channel": "whatsapp",
"to": "+2348012345678",
"status": "failed",
"error": "The sender phone number is not registered on this workspace."
}
Not found (404)
{ "error": "Resource not found." }
Support
- Configure providers in Settings before calling send endpoints.
- Workspace admins: use the in-app Developers page for live base URL, template names, and Postman collection.
- Platform issues: contact your 1app Grow administrator.
Need a workspace with providers configured?
Start free trial