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. Switching to production is swapping one environment
variable.
How it works
Section titled “How it works”Live key (sk_prod_…) | Sandbox key (sk_test_…) | |
|---|---|---|
| Validation & errors | Real | Identical — same codes, same order |
| 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 response has the exact production shape 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 and blocked-country filtering are skipped.
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”Replace the key. That’s the whole migration:
# beforeexport API_KEY=sk_test_abc...# afterexport API_KEY=sk_prod_xyz...