{"id":"sms.batch.send","module":"sms","namespace":"sms","action":"batch.send","method":"POST","path":"/v1/sms/batch/send","idempotent":true,"available":true,"vendors":["twilio","tencent_sms"],"vendors_ready":["tencent_sms"],"vendors_pending":["twilio"],"key_status":"live","default_vendor":"tencent_sms","self_hosted":false,"minimum_tier":"standard","supported_message_classes":null,"regions":["western"],"dynamic_params":{"vendor":["tencent_sms"]},"models_tracked":false,"sdk_hint":"Regular parameters + types are stable — read them from the typed SDK signature / API reference for this capability. discovery only carries the runtime-dynamic values above (available, vendors, dynamic_params).","params":{"title":"SmsBatchSendRequest","description":"Heterogeneous batch send: each item is a full SmsSendRequest (distinct body / recipients / template). One idempotency_key for the whole batch; per-message charges are not double-billed (batch passthrough).","type":"object","required":["messages"],"additionalProperties":false,"properties":{"messages":{"type":"array","minItems":1,"maxItems":100,"items":{"title":"SmsSendRequest","description":"Request shape for infra.sms.send. See docs/phase2/P2_03_New_Modules.md §1.2.","type":"object","required":["to"],"additionalProperties":false,"properties":{"to":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"E.164 phone number or array of."},"body":{"type":["string","null"],"description":"Plain text body of the message"},"from":{"type":["string","null"],"description":"Sender ID, phone number, or approved sign name to use when the vendor path supports it."},"template_id":{"type":["string","null"],"description":"Identifier of the template used for rendering"},"template_vars":{"type":["object","null"],"description":"Variables to substitute in the template"},"vendor":{"type":["string","null"],"description":"Pin to a specific vendor."},"mode":{"enum":["default_vendor"],"default":"default_vendor","description":"Delivery mode or operation mode"},"scheduled_at":{"type":["string","null"],"format":"date-time","description":"ISO 8601 timestamp when the resource is scheduled to activate"},"idempotency_key":{"type":["string","null"],"description":"Client-provided idempotency key; prevents duplicate execution on retry"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags for categorization and filtering"},"route_class":{"enum":["transactional","otp","marketing"],"default":"transactional","description":"SMS route class for delivery optimization"},"opt_in_proof_url":{"type":["string","null"],"description":"Required when route_class=marketing.","format":"uri"}}},"description":"Up to 100 SmsSendRequest items."},"idempotency_key":{"type":["string","null"],"description":"Client-provided idempotency key; prevents duplicate execution on retry"}}},"response":{"title":"SmsBatchSendResult","description":"Per-message outcome of a batch send. results[i] aligns positionally with the request messages[i].","type":"object","required":["results"],"additionalProperties":false,"properties":{"results":{"type":"array","items":{"type":"object","required":["index"],"additionalProperties":false,"properties":{"index":{"type":"integer","minimum":0,"description":"Position in the request messages array."},"result":{"oneOf":[{"title":"SmsSendResult","description":"Response shape for infra.sms.send. See docs/phase2/P2_03_New_Modules.md §1.2.","type":"object","required":["message_id","state","vendor","segments","created_at"],"additionalProperties":false,"properties":{"message_id":{"type":"string","pattern":"^sms_[A-Za-z0-9]{20,}$","description":"Unique identifier for this message"},"state":{"description":"Current lifecycle state of this resource","type":"string","enum":["queued","sending","sent","delivered","failed","deferred","expired","cancelled","auto_suppressed"]},"vendor":{"type":"string","description":"Vendor that handled or will handle this request"},"segments":{"type":"integer","minimum":1,"description":"Long SMS multi-part billing count."},"cost_usd":{"type":["number","null"],"minimum":0,"description":"Cost of this operation in USD"},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when this resource was created"}}},{"type":"null"}],"description":"Send result when this item succeeded."},"error":{"oneOf":[{"title":"ErrorInfo","description":"Machine + human readable error. `code` MUST come from errors/registry.yaml.","type":"object","required":["code","http_status","message","docs_url"],"additionalProperties":false,"properties":{"code":{"type":"string","description":"Stable identifier from registry.yaml."},"http_status":{"type":"integer","minimum":400,"maximum":599,"description":"HTTP status code of the webhook delivery attempt"},"message":{"type":"string","description":"Human-readable hint."},"docs_url":{"type":"string","format":"uri","description":"Documentation URL for this error code"},"code_detail":{"type":"string","description":"Sub-classification (e.g. wallet cap reason)."},"retryable":{"type":"boolean","description":"Whether the error is retryable"},"retry_after_ms":{"type":"integer","minimum":0,"description":"Suggested retry delay in milliseconds"},"param":{"type":"string","description":"Which parameter failed validation, if applicable."},"trace_id":{"type":"string","description":"Distributed tracing identifier"},"request_id":{"type":"string","description":"Server-assigned request identifier for tracing"}}},{"type":"null"}],"description":"Error when this item failed; result is null."}}},"description":"List of individual results from a batch operation"}}},"errors":["ACCOUNT_AUTORECHARGE_LIMIT","ACCOUNT_FROZEN","AUTH_RATE_LIMIT","AUTH_REFRESH_TOO_FREQUENT","IDEMPOTENCY_KEY_CONFLICT","INSUFFICIENT_CREDIT","INTERNAL_ERROR","INVALID_ARGUMENT","INVALID_PHONE_NUMBER","KEY_REVOKED","MAINTENANCE","NETWORK_ERROR","RATE_LIMIT_ACCOUNT","RATE_LIMIT_USER","RATE_LIMIT_VENDOR","SCOPE_INSUFFICIENT","SMS_CONFIRMATION_REQUIRED","SMS_CONTENT_REJECTED","SMS_INBOUND_NOT_SUPPORTED","SMS_INVALID_RETENTION","SMS_MESSAGE_NOT_FOUND","SMS_NOT_CONFIGURED","SMS_RATE_LIMIT","SMS_SEGMENT_LIMIT_EXCEEDED","SMS_SENDER_NOT_REGISTERED","SMS_SEND_FAILED","SMS_SIGNATURE_NOT_FOUND","SMS_SUPPRESSED_RECIPIENT","SMS_TEMPLATE_NOT_APPROVED","SMS_TEMPLATE_NOT_FOUND","SMS_TEMPLATE_REQUIRED","SMS_VENDOR_DOWN","SMS_VENDOR_UNSUPPORTED_OP","UNAUTHORIZED","VENDOR_AUTH_ERROR","VENDOR_DOWN","VENDOR_TIMEOUT","WALLET_EXPIRED"],"examples":{"curl":"curl -X POST https://api.infrai.cc/v1/sms/batch/send \\\n  -H \"Authorization: Bearer $INFRAI_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"messages\": [{\"to\": \"sample\"}]}'","python":"# Zero-install REST call — no SDK required (short-term the API is REST-only).\nimport os, requests\n\nresp = requests.post(\n    \"https://api.infrai.cc/v1/sms/batch/send\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['INFRAI_API_KEY']}\",\n    },\n    json={'messages': [{'to': 'sample'}]},\n)\nresp.raise_for_status()\nprint(resp.json())","javascript":"// Zero-install REST call — no SDK required (short-term the API is REST-only).\nconst resp = await fetch(\n  \"https://api.infrai.cc/v1/sms/batch/send\",\n  {\n    method: \"POST\",\n    headers: {\n      \"Authorization\": `Bearer ${process.env.INFRAI_API_KEY}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\"messages\": [{\"to\": \"sample\"}]}),\n  },\n);\nconsole.log(await resp.json());","typescript":"// Zero-install REST call — no SDK required (short-term the API is REST-only).\nconst resp = await fetch(\n  \"https://api.infrai.cc/v1/sms/batch/send\",\n  {\n    method: \"POST\",\n    headers: {\n      \"Authorization\": `Bearer ${process.env.INFRAI_API_KEY}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\"messages\": [{\"to\": \"sample\"}]}),\n  },\n);\nif (!resp.ok) throw new Error(`infrai ${resp.status}`);\nconst data: unknown = await resp.json();\nconsole.log(data);","go":"// Zero-install REST call — no SDK required (short-term the API is REST-only).\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n)\n\nfunc main() {\n\tbody := []byte(`{\"messages\": [{\"to\": \"sample\"}]}`)\n\treq, _ := http.NewRequest(\"POST\", \"https://api.infrai.cc/v1/sms/batch/send\", bytes.NewReader(body))\n\treq.Header.Set(\"Authorization\", \"Bearer \"+os.Getenv(\"INFRAI_API_KEY\"))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\tfmt.Println(resp.Status)\n}","java":"// Zero-install REST call — no SDK required (short-term the API is REST-only).\nimport java.net.URI;\nimport java.net.http.*;\n\nHttpRequest req = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.infrai.cc/v1/sms/batch/send\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"INFRAI_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\\"messages\\\": [{\\\"to\\\": \\\"sample\\\"}]}\"))\n    .build();\nHttpResponse<String> resp = HttpClient.newHttpClient()\n    .send(req, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(resp.body());","csharp":"// Zero-install REST call — no SDK required (short-term the API is REST-only).\nusing System;\nusing System.Net.Http;\n\nvar client = new HttpClient();\nvar req = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.infrai.cc/v1/sms/batch/send\");\nvar key = Environment.GetEnvironmentVariable(\"INFRAI_API_KEY\");\nreq.Headers.Add(\"Authorization\", \"Bearer \" + key);\nreq.Content = new StringContent(\"{\\\"messages\\\": [{\\\"to\\\": \\\"sample\\\"}]}\", System.Text.Encoding.UTF8, \"application/json\");\nvar resp = await client.SendAsync(req);\nConsole.WriteLine(await resp.Content.ReadAsStringAsync());","php":"<?php\n// Zero-install REST call — no SDK required (short-term the API is REST-only).\n$ch = curl_init(\"https://api.infrai.cc/v1/sms/batch/send\");\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\n    \"Authorization: Bearer \" . getenv(\"INFRAI_API_KEY\"),\n    \"Content-Type: application/json\",\n]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, \"{\\\"messages\\\": [{\\\"to\\\": \\\"sample\\\"}]}\");\n$response = curl_exec($ch);\ncurl_close($ch);\necho $response;","ruby":"# Zero-install REST call — no SDK required (short-term the API is REST-only).\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(\"https://api.infrai.cc/v1/sms/batch/send\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\nreq = Net::HTTP::Post.new(uri)\nreq[\"Authorization\"] = \"Bearer #{ENV['INFRAI_API_KEY']}\"\nreq[\"Content-Type\"] = \"application/json\"\nreq.body = '{\"messages\": [{\"to\": \"sample\"}]}'\nres = http.request(req)\nputs res.body","rust":"// Zero-install REST call — no SDK required (short-term the API is REST-only). (uses the reqwest + tokio crates)\nuse std::env;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let resp = reqwest::Client::new()\n        .post(\"https://api.infrai.cc/v1/sms/batch/send\")\n        .header(\"Authorization\", format!(\"Bearer {}\", env::var(\"INFRAI_API_KEY\")?))\n        .header(\"Content-Type\", \"application/json\")\n        .body(r#\"{\"messages\": [{\"to\": \"sample\"}]}\"#)\n        .send()\n        .await?;\n    println!(\"{}\", resp.text().await?);\n    Ok(())\n}","request":{"messages":[{"to":"sample"}]}},"billing":{"is_billable":true,"free":false,"billing_class":"service_markup","unit":"per_message","price_usd":0.008395,"currency":"USD","approximate":true,"new_account_trial_uses":238,"note":"default vendor tencent_sms: $0.008395 per_message; other vendor prices may differ","price_vendor":"tencent_sms"}}