Sandbox & test mode
Copy page
Sandbox keys (sk_test_…) let you build and test your entire integration —
sending, status polling, and webhook handling — without sending real SMS
or spending money. For the standard API endpoints, switching to production is
swapping one environment variable. CRM integrations use the managed activation
steps in their platform guide.
How it works
Section titled “How it works”Live key (sk_prod_…) | Sandbox key (sk_test_…) | |
|---|---|---|
| Validation & errors | Real | Same request validation; production account/cost lookup and blocked-country filtering are skipped |
| Segments & encoding | Real | Identical (GSM-7/UCS-2 detection) |
| Cost preview | Real rates | Real rates when cached, else 0 |
| SMS delivery | Real carrier | Simulated — nothing is sent |
| Balance | Deducted | Never touched |
| Delivery webhook | Real DLR | Synthesized DELIVRD ~5s after send, same HMAC signing |
| Message storage | Permanent history | 24 hours, last 500 messages |
| Response | — | Carries "sandbox": true |
-
Create a sandbox key. In the portal under Settings → API Keys, enable Test mode when creating the key. You get an
sk_test_…key. -
Send a simulated SMS — same request as production:
Terminal window curl -X POST https://restlink23telecom.com/api/v1/sms/send \-H "X-API-Key: $API_KEY" \-H "Content-Type: application/json" \-d '{"to": ["+447911123456"],"message": "Sandbox hello!","sender_id": "MyApp"}'const res = await fetch('https://restlink23telecom.com/api/v1/sms/send', {method: 'POST',headers: { 'X-API-Key': process.env.API_KEY, 'Content-Type': 'application/json' },body: JSON.stringify({"to": ["+447911123456"],"message": "Sandbox hello!","sender_id": "MyApp"}),});if (!res.ok) {const err = await res.json();throw new Error(`${err.error_code}: ${err.description}`);}const data = await res.json();console.log(data);import osimport requestsres = requests.post("https://restlink23telecom.com/api/v1/sms/send",headers={"X-API-Key": os.environ["API_KEY"]},json={"to": ["+447911123456"],"message": "Sandbox hello!","sender_id": "MyApp"},)res.raise_for_status()print(res.json())<?php$ch = curl_init('https://restlink23telecom.com/api/v1/sms/send');curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => 'POST',CURLOPT_RETURNTRANSFER => true,CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('API_KEY'), 'Content-Type: application/json'],CURLOPT_POSTFIELDS => json_encode(['to' => ['+447911123456'],'message' => 'Sandbox hello!','sender_id' => 'MyApp']),]);$response = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);if ($status !== 200) {throw new Exception("HTTP $status: $response");}$data = json_decode($response, true);print_r($data);require "net/http"require "json"uri = URI("https://restlink23telecom.com/api/v1/sms/send")req = Net::HTTP::Post.new(uri)req["X-API-Key"] = ENV.fetch("API_KEY")req["Content-Type"] = "application/json"req.body = {to: ["+447911123456"],message: "Sandbox hello!",sender_id: "MyApp"}.to_jsonres = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }raise "HTTP #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)puts JSON.parse(res.body)import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://restlink23telecom.com/api/v1/sms/send")).header("X-API-Key", System.getenv("API_KEY")).header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString("""{"to": ["+447911123456"],"message": "Sandbox hello!","sender_id": "MyApp"}""")).build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());package mainimport ("fmt""io""net/http""os""strings")func main() {payload := strings.NewReader(`{"to": ["+447911123456"],"message": "Sandbox hello!","sender_id": "MyApp"}`)req, err := http.NewRequest("POST", "https://restlink23telecom.com/api/v1/sms/send", payload)if err != nil {panic(err)}req.Header.Set("X-API-Key", os.Getenv("API_KEY"))req.Header.Set("Content-Type", "application/json")res, err := http.DefaultClient.Do(req)if err != nil {panic(err)}defer res.Body.Close()data, _ := io.ReadAll(res.Body)fmt.Println(string(data))}using System.Text;using var client = new HttpClient();client.DefaultRequestHeaders.Add("X-API-Key",Environment.GetEnvironmentVariable("API_KEY"));var json = """{"to": ["+447911123456"],"message": "Sandbox hello!","sender_id": "MyApp"}""";var content = new StringContent(json, Encoding.UTF8, "application/json");var response = await client.PostAsync("https://restlink23telecom.com/api/v1/sms/send", content);response.EnsureSuccessStatusCode();Console.WriteLine(await response.Content.ReadAsStringAsync());The success response has the production fields plus
"sandbox": true. -
Watch the delivery report arrive. About 5 seconds later your configured
delivery_urlreceives a synthesizedDELIVRDwebhook with a valid HMAC signature — verify it exactly like a real one (securing webhooks). -
Poll status if you prefer:
GET /sms/status/{message_id}returnssent, thenDELIVRDafter the simulated carrier latency.
What is intentionally different
Section titled “What is intentionally different”- Sandbox message history is kept for 24 hours (last 500 per workspace) — it never appears in production statistics or exports.
- URL auto-shortening, blocked-country filtering and production account/cost lookup are skipped. Live pricing or balance cache misses are fail-open, not local send gates.
GET /sms/messagessupports pagination but not SQL-grade filters.- If the simulator is unavailable you get
503 SANDBOX_UNAVAILABLE— a test key never falls through to the live pipeline.
Going live
Section titled “Going live”For the standard /sms/send and /sms/send-individual endpoints, replace the
key. That’s the whole migration:
# beforeexport API_KEY=sk_test_abc...# afterexport API_KEY=sk_prod_xyz...For /integrations/sms/send and the CRM platform guides, follow the page’s
Production activation section. Your account manager enables the exact
workspace and, where required, supplies the approved production URL or adapter
mapping. Do not promote a legacy CRM batch template by changing only its key.