API documentation

Requests, responses, and signed webhooks.

API Settings
On this page

Quick start

An SMS Kit is the service you activate and manage. Its associated phone number identifies it in phone-based API requests.

Copy your token from API Settings. Send POST requests to https://www.groovl.com/api.php with JSON and a Bearer token. API access is available to all active accounts.

curl --request POST 'https://www.groovl.com/api.php' \
  --header 'Authorization: Bearer YOUR_API_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"resource":"balance"}' 
{
  "resource": "balance",
  "resolution": "ok",
  "balance": "100.0000"
}

Authentication

Authorization: Bearer YOUR_API_TOKEN

The token identifies your account and is case-sensitive. Do not include user, token, or interface in the request body.

Replacing your API token invalidates the old one immediately. Your webhook signing secret is separate and does not change when you replace your API token.

Request and response rules

  • Use POST with Content-Type: application/json. Send one JSON object, up to 65,536 bytes.
  • Include resource. Unknown fields are rejected. Use strings as shown in the examples.
  • Use digits only for final phone numbers and uppercase two- or three-letter country codes.
  • Check resolution, even with HTTP 200: ok, pending, or failed. Acceptance does not confirm completion of activation or cancellation.
  • Amounts are in Credits. Dates use YYYY-MM-DD HH:MM:SS without a timezone offset; do not assume UTC.

Resources

ResourceRequiredOptionalBehavior
balanceNoneNoneRead
countriesNoneNoneRead
searchcountryphoneRead
planscountryNoneRead
activatepidplanCompletes after acceptance
listNonephone, status, page, autorenew, countryRead
settingsphoneNoneRead
updatephone, name, to, addressNoneImmediate
autorenewphone, autorenewNoneImmediate
planphone, planNoneNext renewal
cancelphoneNoneCompletes after acceptance

balance

Get your account balance in Credits. Credits committed to pending requests may still appear in this balance but are unavailable to spend.

Request

{
  "resource": "balance"
}

Response

{
  "resource": "balance",
  "resolution": "ok",
  "balance": "100.0000"
}

countries

List countries with available SMS Kits, ordered by country name. Requires a Bearer token; no additional parameters or pagination. Each country appears once. Names and telephone prefixes come from the country directory; availability follows the same inventory rules as search.

Request

{
  "resource": "countries"
}

Response

{
  "resource": "countries",
  "resolution": "ok",
  "response": [
    {
      "country2x": "FR",
      "title": "France",
      "prefix": "33"
    },
    {
      "country2x": "GB",
      "title": "United Kingdom",
      "prefix": "44"
    }
  ]
}

country2x is the two- or three-letter country code to send as country in search and plans. prefix is a string without the leading +. If no countries have available SMS Kits, the response is resolution="ok" with response=[]. Availability can change before activation; displaying a country or number does not reserve it.

plans

List plans for the required country, ordered by renewal fee. premium="1" requires a Premium account. Account-specific pricing may apply.

Request

{
  "resource": "plans",
  "country": "FR"
}

Response

{
  "resource": "plans",
  "resolution": "ok",
  "response": [
    {
      "plan": "PLAN_ID",
      "country": "FR",
      "title": "PLAN_TITLE",
      "days": "30",
      "setup": "0.0000",
      "renew": "159.0000",
      "premium": "0",
      "inbound_sms": "PLAN_VALUE",
      "outbound_sms": "n/a"
    }
  ]
}

activate

Activate a search result. Required: pid. Optional: plan; defaults to the lowest-renewal-fee eligible non-Premium plan for the country. Available Credits must cover setup and the first renewal.

Request

{
  "resource": "activate",
  "pid": "NUMBER_ID",
  "plan": "1"
}

Response

{
  "resource": "activate",
  "resolution": "ok",
  "pid": "NUMBER_ID",
  "plan": "1",
  "phone": "33600000000",
  "terms": "PLAN_TERMS",
  "response": "Request has been sent and will be done in a couple of minutes"
}

Save pid before sending the request. Confirm completion through the activation webhook. For pending, see delayed activation.

Delayed activation

Some carriers need additional setup. A pending response includes a masked number and the original pid. Keep the request open; do not resubmit it or use the masked number in phone-based operations.

Pending response

{
  "resource": "activate",
  "resolution": "pending",
  "pid": "NUMBER_ID",
  "plan": "1",
  "phone": "(+1) 202-55X-XXXX",
  "terms": "PLAN_TERMS",
  "response": "Your request is in and may take up to 24 hours to complete, but we'll work to get it done sooner"
}

Completion webhook

{
  "user": "PUBLIC_ACCOUNT_ID",
  "action": "plan setup",
  "resolution": "done",
  "phone": "12025550123",
  "date": "2026-10-15 12:00:00",
  "pid": "NUMBER_ID"
}

Verify the signature, match pid to your saved request, and replace the masked value with the final phone. Set up your webhook before requesting activation.

list

List your SMS Kits and associated phone numbers. Returns up to 10 per page, ordered by expiration date.

Optional fieldValues
status1 Allocation, 2 Live (default), 3 Released, 4 Storage
pageStarts at 1
phonePrefix; digits and * wildcard
countryTwo- or three-letter country code
autorenew0 off, 1 on

Request

{
  "resource": "list",
  "status": "2",
  "page": "1"
}

Response

{
  "resource": "list",
  "resolution": "ok",
  "pages": "1",
  "page": "1",
  "response": [
    {
      "phone": "33600000000",
      "status": "2",
      "country": "FR",
      "expires": "2026-11-15 12:00:00",
      "autorenew": "1",
      "plan": "1"
    }
  ]
}

No matches return failed with Nothing to display yet.

settings

Read an active SMS Kit’s settings using its phone number. Required: phone. The SMS Kit must belong to your account.

Request

{
  "resource": "settings",
  "phone": "33600000000"
}

Response

{
  "resource": "settings",
  "resolution": "ok",
  "phone": "33600000000",
  "country": "FR",
  "name": "My SMS Kit",
  "expires": "2026-11-15 12:00:00",
  "plan": "1",
  "fee": "159.0000",
  "autorenew": "1",
  "to": "0",
  "address": ""
}

update

Update an active SMS Kit’s label and SMS forwarding settings. All fields are required: phone, name (up to 255 bytes; may be empty), to, and address (up to 2,048 bytes).

toaddress
0Empty string; turns forwarding off
1Email address
2Public HTTPS callback URL on port 443

Request

{
  "resource": "update",
  "phone": "33600000000",
  "name": "My SMS Kit",
  "to": "0",
  "address": ""
}

Response

{
  "resource": "update",
  "resolution": "ok",
  "phone": "33600000000",
  "response": "Updated"
}

See SMS forwarding for delivery details.

SMS forwarding to a URL

Use update to set a forwarding URL for each active SMS Kit. Set to to "2", and include the SMS Kit’s current name to keep its label.

Request

{
  "resource": "update",
  "phone": "33600000000",
  "name": "My SMS Kit",
  "to": "2",
  "address": "https://your-site.com/sms"
}

Response

{
  "resource": "update",
  "resolution": "ok",
  "phone": "33600000000",
  "response": "Updated"
}

Use settings to check the saved forwarding configuration. To disable forwarding, send update with to="0" and address="".

Use a public HTTPS hostname on port 443 with a valid certificate and IPv4 support. Redirects are not followed. This URL receives SMS; your account Webhook URL receives SMS Kit events.

Incoming SMS

We POST JSON to your URL with Content-Type: application/json:

{
  "to": "33600000000",
  "from": "12025550123",
  "text": "Your verification code is 123456.",
  "id": "4d139a85327e09f9aafc5183b328156a"
}

All four fields are strings. to is your number, from is the sender’s number or alphanumeric name, text is the message, and id identifies the SMS.

Verify the signature with your account’s webhook signing secret from API Settings. Use X-Proovl-Event-Id for signature verification and the JSON id to recognize duplicate SMS. These are different IDs. No API token is sent.

SMS callbacks contain text messages only. Non-text messages, such as binary carrier settings, are not forwarded.

After verification, save the message and return 204 No Content (or another 2xx status) within 10 seconds. Return 2xx for an SMS you already saved, without processing it again. We ignore the response body.

SMS forwarding runs from a queue, with up to six attempts. Timeouts and non-2xx responses are retried after approximately 1, 5, 15, 60, and 360 minutes. Retry-After on HTTP 429 or 503 can extend the delay. Retries keep the same SMS and event IDs; return 2xx for an SMS already saved. Each attempt uses your current forwarding URL and signing secret. Disabling forwarding or deactivating the SMS Kit or account stops queued delivery when checked by the worker. Your SMS remains available in your account if forwarding fails.

autorenew

Set auto-renew for your active SMS Kit. Required: phone, autorenew (1 on, 0 off). Takes effect immediately without charging or renewing the SMS Kit.

Request

{
  "resource": "autorenew",
  "phone": "33600000000",
  "autorenew": "1"
}

Response

{
  "resource": "autorenew",
  "resolution": "ok",
  "phone": "33600000000",
  "autorenew": "1",
  "response": "Auto renew ON"
}

An unchanged setting returns failed with Nothing to update.

plan

Choose the plan for your active SMS Kit’s next renewal. Required: phone, plan. Country, account eligibility, and available Credits must match the new plan. No immediate charge.

Request

{
  "resource": "plan",
  "phone": "33600000000",
  "plan": "1"
}

Response

{
  "resource": "plan",
  "resolution": "ok",
  "phone": "33600000000",
  "plan": "1",
  "response": "PLAN_TITLE will start after 2026-11-15 12:00:00"
}

Choosing the current plan returns Nothing to update.

cancel

Cancel your active SMS Kit using its phone number. Required: phone. This ends the service, rather than just turning off auto-renew. Confirm completion through a webhook or SMS Kit status.

Request

{
  "resource": "cancel",
  "phone": "33600000000"
}

Response

{
  "resource": "cancel",
  "resolution": "ok",
  "phone": "33600000000",
  "response": "Request has been sent"
}

A pending cancellation returns failed with Request already sent.

Errors and retries

HTTPAction
200Check resolution; the operation may have failed.
400Check the JSON, resource, and fields.
401Check your token and account status.
405Use POST.
413Keep the body within 65,536 bytes.
415Use Content-Type: application/json.
429Wait for Retry-After.
500 / 503Check operation status before retrying a write. Follow Retry-After when present.

There is no general idempotency-key parameter. After a timeout, check the SMS Kit’s status and webhooks before repeating a request.

responseMeaning
Number not foundNot owned by you or unavailable for this operation.
Number not availableCannot be activated.
Insufficient funds, please refill your balanceNot enough available Credits.
Nothing to updateThe setting already matches.
Request already sentA matching request is still pending.

Webhooks

Set your Webhook URL in API Settings. Use HTTPS on port 443 with a hostname that resolves to a public IPv4 address. Redirects, IP-only URLs, private addresses, and URL credentials are unsupported. An empty URL disables delivery.

actionEvent
plan setupActivation
plan auto renewAuto-renewal
plan cancelationCancellation
{
  "user": "PUBLIC_ACCOUNT_ID",
  "action": "plan auto renew",
  "resolution": "done",
  "phone": "33600000000",
  "date": "2026-10-15 12:00:00"
}

resolution is done or failed. user identifies the account. date is the recorded action timestamp, not the delivery time or a guaranteed completion time. Final activation and cancellation events include pid.

Accepting events

  • Verify the signature before processing.
  • Save the event and return HTTP 2xx within 10 seconds.
  • Deduplicate by X-Proovl-Event-Id. Return 2xx for an already processed event without repeating its effects.
  • Failed delivery is retried up to six total attempts. Delays after attempts 1–5 are approximately 1, 5, 15, 60, and 360 minutes. HTTP 429 or 503 with Retry-After can extend the delay.

Replacing the signing secret

Use Replace signing secret in API Settings, then update your receiver. New attempts, including retries, use the new secret; an in-flight attempt may still use the old one. Your API token is unchanged.

Verify webhook signatures

The same verification applies to SMS callbacks and action webhooks.

HeaderValue
X-Proovl-Event-IdEvent ID; unchanged across retries
X-Proovl-TimestampUnix seconds for this delivery attempt
X-Proovl-Signaturev1= + lowercase HMAC-SHA256 hex

Calculate HMAC-SHA256 over timestamp + "." + eventId + "." + rawBody. Use the displayed signing secret as the literal key; do not hex-decode it or re-encode the JSON.

PHP example: load $secret from your private configuration.

$body = file_get_contents('php://input');
$event = $_SERVER['HTTP_X_PROOVL_EVENT_ID'] ?? '';
$timestamp = $_SERVER['HTTP_X_PROOVL_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_PROOVL_SIGNATURE'] ?? '';
if (!is_string($secret) || !preg_match('/^[a-f0-9]{64}$/D', $secret)
    || !preg_match('/^[1-9][0-9]{0,19}$/D', $event)
    || !preg_match('/^[0-9]{10}$/D', $timestamp)
    || !preg_match('/^v1=[a-f0-9]{64}$/D', $signature)
    || abs(time() - (int)$timestamp) > 300
    || !hash_equals('v1=' . hash_hmac('sha256', $timestamp . '.' . $event . '.' . $body, $secret), $signature)) {
    http_response_code(401);
    exit;
}
// Only now decode and validate JSON, then process the event.

Keep your server clock synchronized. After verification, validate the JSON and save it before returning 2xx. Deduplicate SMS by the JSON id and SMS Kit events by X-Proovl-Event-Id, keeping those keys separate. SMS Kit event retries keep the event ID but use a new timestamp and signature.

Moving from the previous API

  • Use Bearer authentication. Remove user, token, and interface from request JSON.
  • Check both HTTP status and resolution.
  • Verify SMS callbacks and SMS Kit event webhooks with your webhook signing secret, not your API token.
  • Save activation pid values and deduplicate webhook events.