
Alibabacloud Sms Send Short Message
- 98 installs
- 208 repo stars
- Updated August 4, 2026
- aliyun/alibabacloud-aiops-skills
alibabacloud-sms-send-short-message is a Claude skill that sends SMS verification codes, notifications and marketing messages via Alibaba Cloud Short Message Service through the aliyun CLI.
About
This skill sends short messages through Alibaba Cloud Short Message Service on top of the aliyun CLI. A developer uses it to send verification codes, notifications, or marketing campaigns, either as a single send to up to 1000 numbers sharing a signature and template, or a batch send of up to 100 numbers each with its own signature and params. It can check signature and template approval status before sending to improve delivery success.
- Sends SMS verification codes, notifications and marketing messages via Alibaba Cloud SMS
- Supports single send (up to 1000 numbers) and batch send (up to 100 numbers)
- Verifies signature and template approval status before sending
Alibabacloud Sms Send Short Message by the numbers
- 98 all-time installs (skills.sh)
- Ranked #2,991 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
alibabacloud-sms-send-short-message capabilities & compatibility
Free skill; requires an Alibaba Cloud account with SMS service activated (messages billed by Alibaba Cloud).
- Capabilities
- sms sending · batch messaging · template management
- Use cases
- marketing · email
- Runs
- Runs locally
- Pricing
- Bring your own API key
What alibabacloud-sms-send-short-message says it does
Send short messages (SMS) to specified phone numbers via Alibaba Cloud Short Message Service.
The signature and template approval status can be verified before sending to maximize
npx skills add https://github.com/aliyun/alibabacloud-aiops-skills --skill alibabacloud-sms-send-short-messageAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 98 |
|---|---|
| repo stars | ★ 208 |
| Last updated | August 4, 2026 |
| Repository | aliyun/alibabacloud-aiops-skills ↗ |
What it does
Send SMS verification codes, notifications and marketing messages through Alibaba Cloud SMS via the aliyun CLI.
Who is it for?
Sending SMS verification codes, notifications and marketing messages through Alibaba Cloud SMS.
When should I use this skill?
You need to send SMS verification codes, order reminders, notifications, or a marketing campaign.
What you get
Approved SMS messages are sent to one or many recipients with signature and template status verified first.
By the numbers
- SendSms handles up to 1000 phone numbers
- SendBatchSms handles up to 100 phone numbers
Files
Critical CLI Compliance (Must Read First)
The following declarations apply to every aliyun CLI invocation in this Skill — they must be honored before any send / query operation, workflow step, or functional scenario described later in this document.
Pre-check: Aliyun CLI >= 3.3.3 required
Run aliyun version to verify the version is >= 3.3.3. If not installed or the versionis too low, run curl -fsSL https://aliyuncli.alicdn.com/setup.sh | bash to install orupdate.
Pre-check: Aliyun CLI plugin update required
[MUST] run aliyun configure set --auto-plugin-install true to enable automatic plugininstallation.
[MUST] run aliyun plugin update to ensure that any existing plugins on your localmachine are always up-to-date.
[MUST] Enable AI-Mode — AI-mode is required for Agent Skill execution. Run the following commands before any CLI invocation:
aliyun configure ai-mode enable
aliyun configure ai-mode set-user-agent --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message"[MUST] Disable AI-Mode at EVERY exit point — Before delivering the final response for ANY reason, always disable AI-mode first. This applies to ALL exit paths: workflow success, workflow failure, error/exception, user cancellation, session end, or any other scenario where no further CLI commands will be executed. AI-mode is only used for Agent Skill invocation scenarios and MUST NOT remain enabled after the skill stops running.
aliyun configure ai-mode disable[MUST] CLI User-Agent — Every aliyun CLI command invocation in this Skill must include the flag: --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message
---
Overview
This Skill wraps the Alibaba Cloud Short Message Service (SMS) end-to-end on top of the aliyun CLI, exposing the following capabilities:
- Send SMS: verification codes, notifications, or marketing messages (SendSms).
- Batch send: different signatures / template params per phone number (SendBatchSms).
- Signature management: query approval status and signature lists.
- Template management: query approval status and template lists.
- Send status query: query SMS send details and delivery receipts.
- Send statistics query: aggregated send / success / fail counts in a date range
(QuerySendStatistics).
- SMS qualification query: list records (QuerySmsQualificationRecord) and
single-record detail (QuerySingleSmsQualification) — see `references/sms-qualification.md`.
Interactive Parameter Confirmation (Must Read)
Before executing any send-class operation (send-sms / send-batch-sms), this Skill must collect these critical parameters first:
--sign-name(signature name)--template-code(template code)--template-param(template variables, when the template contains placeholders)--phone-numbers/--phone-number-json(phone numbers)
Handling Rules
When the user does not explicitly provide the signature or template, the AI agent must not guess or fabricate them. Instead it should:
1. Ask the user proactively: "Which signature/template should I use? If unsure, I can list approved ones from your account for you to pick from." 2. After the user agrees, call list APIs:
aliyun dysmsapi query-sms-sign-list ...thenaliyun dysmsapi get-sms-sign --sign-name <name>per candidate.aliyun dysmsapi query-sms-template-list ...- Hard filter: keep only signs with
SignStatus = 1(approved) and templates withTemplateStatus = 1. Do NOT drop signs based on carrier registration alone — unregistered carriers may still deliver. Instead, surfaceSignIspRegisterDetailList[*].RegisterResult(per-carrier registration status) as an extra column in the candidate table and let the user decide. Columns: index / code / name / content /SignStatus/ per-carrier registration.
3. Resolve template variables: If the picked template contains ${xxx} placeholders, ask for each variable's value one by one and assemble them into --template-param JSON. 4. Phone numbers: Always require explicit phone numbers from the user; never reuse placeholder numbers like 13800138000 from this document as the actual recipient. 5. Final confirmation: Once all critical parameters are collected, restate the complete send plan and only call `send-sms` / `send-batch-sms` after the user gives an explicit confirmation (e.g. "confirm" / "send").
Quick Recovery When Signature / Template Is Invalid
If send-sms returns isv.SMS_SIGN_NAME_ILLEGAL / isv.SMS_TEMPLATE_ILLEGAL, the agent should proactively offer to "list existing signatures and templates" and, after the user agrees, call the list APIs instead of retrying blindly.
Prerequisites
1. Install aliyun CLI (>= 3.3.3)
# macOS
brew install aliyun-cli
# Linux
wget https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
tar -xzf aliyun-cli-linux-latest-amd64.tgz && sudo mv aliyun /usr/local/bin/
aliyun version # must be >= 3.3.32. Install the dysmsapi plugin
aliyun plugin install --names dysmsapi3. Configure Credentials
The aliyun CLI default credential chain is recommended; it picks credentials in this priority order: env vars → ~/.aliyun/config.json → ECS RAM role → OIDC Token.
# Recommended for CI/CD: env vars
export ALIBABA_CLOUD_ACCESS_KEY_ID=<your-access-key-id>
export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<your-access-key-secret>
export ALIBABA_CLOUD_REGION_ID=cn-hangzhou
# Verify
aliyun configure getImportant: Never hard-code AK/SK in code or commands. Always rely on the default
credential chain. On ECS use a RAM role; in K8s use OIDC.
4. Activate the SMS Service
In the Alibaba Cloud SMS Console: activate the service, create an approved signature, and create an approved template.
Usage
Calling aliyun CLI directly
Send SMS:
aliyun dysmsapi send-sms \
--api-version 2017-05-25 \
--phone-numbers "13800138000" \
--sign-name "AliyunDemo" \
--template-code "SMS_123456" \
--template-param '{"code":"123456"}' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message \
--read-timeout 3Batch send (different signatures / template params):
aliyun dysmsapi send-batch-sms \
--api-version 2017-05-25 \
--phone-number-json '["13800138000","13900139000"]' \
--sign-name-json '["SignA","SignB"]' \
--template-code "SMS_123456" \
--template-param-json '[{"code":"111111"},{"code":"222222"}]' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message \
--read-timeout 3Query lists / status / details:
# Approved signatures / templates (use to populate user choices)
aliyun dysmsapi query-sms-sign-list --api-version 2017-05-25 --page-index 1 --page-size 50 --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
aliyun dysmsapi query-sms-template-list --api-version 2017-05-25 --page-index 1 --page-size 50 --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# Single signature / template lookup
aliyun dysmsapi get-sms-sign --api-version 2017-05-25 --sign-name "AliyunDemo" --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
aliyun dysmsapi get-sms-template --api-version 2017-05-25 --template-code "SMS_123456" --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# Per-message delivery details (use BizId for precise lookup)
aliyun dysmsapi query-send-details \
--api-version 2017-05-25 \
--phone-number "13800138000" --send-date "20260326" \
--page-size 10 --current-page 1 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# Aggregated statistics (--is-globe: 1=domestic, 2=intl/HK-Macao-Taiwan)
aliyun dysmsapi query-send-statistics \
--api-version 2017-05-25 --is-globe 1 \
--start-date "20260301" --end-date "20260326" \
--page-index 1 --page-size 10 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# Filter by signature name + template type (optional)
aliyun dysmsapi query-send-statistics \
--api-version 2017-05-25 --is-globe 1 \
--start-date "20260301" --end-date "20260326" \
--page-index 1 --page-size 10 \
--sign-name "AliyunDemo" --template-type 0 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# SMS qualification — list records (full detail in references/sms-qualification.md)
aliyun dysmsapi query-sms-qualification-record \
--api-version 2017-05-25 --page-no 1 --page-size 20 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# SMS qualification — single detail by GroupId
aliyun dysmsapi query-single-sms-qualification \
--api-version 2017-05-25 --qualification-group-id 10000123 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Full filter parameters, response schemas, and state enum mapping for the
qualification APIs are documented in
`references/sms-qualification.md`.
Using the wrapper script
scripts/send_sms.sh automatically applies --api-version, the compliant --user-agent, and --read-timeout:
# Send (single or comma-separated phones)
./scripts/send_sms.sh send -p "13800138000" -s "AliyunDemo" -t "SMS_123456" -tp '{"code":"123456"}'
# Send with signature/template pre-verification (recommended)
./scripts/send_sms.sh send -p "13800138000" -s "AliyunDemo" -t "SMS_123456" -tp '{"code":"123456"}' --verify
# List & inspect
./scripts/send_sms.sh list-signs
./scripts/send_sms.sh list-templates
./scripts/send_sms.sh query-sign --sign-name "AliyunDemo"
./scripts/send_sms.sh query-template --template-code "SMS_123456"
# Status & statistics
./scripts/send_sms.sh query-status -p "13800138000" --send-date "20260326" --biz-id "xxx^0"
./scripts/send_sms.sh query-statistics --start-date "20260301" --end-date "20260326"
./scripts/send_sms.sh query-statistics --start-date "20260301" --end-date "20260326" --is-globe 2
# Filter by signature name and/or template type
./scripts/send_sms.sh query-statistics --start-date "20260301" --end-date "20260326" --sign-name "AliyunDemo" --template-type 0Parameters
aliyun dysmsapi send-sms
| Parameter | Required | Description |
|---|---|---|
| --api-version | Yes | API version, fixed to 2017-05-25 |
| --phone-numbers | Yes | Phone numbers, comma-separated, up to 1000 |
| --sign-name | Yes | SMS signature name |
| --template-code | Yes | SMS template code |
| --template-param | No | Template variables JSON, e.g. '{"code":"123456"}' |
| --out-id | No | External tracking ID |
| --user-agent | Yes | Fixed to AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message |
| --read-timeout | Yes | Read timeout in seconds, recommended value 3 |
aliyun dysmsapi send-batch-sms (batch send)
| Parameter | Required | Description |
|---|---|---|
| --api-version | Yes | API version, fixed to 2017-05-25 |
| --phone-number-json | Yes | Phone numbers JSON array, up to 100, e.g. '["138xxx","139xxx"]' |
| --sign-name-json | Yes | Signature names JSON array, must match the phone count |
| --template-code | Yes | Template code (all phones share the same template) |
| --template-param-json | No | Template variables JSON array, must match the phone count |
| --sms-up-extend-code-json | No | Upstream SMS extension codes JSON array |
| --out-id | No | External tracking ID, less than 256 characters |
| --user-agent | Yes | Fixed to AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message |
| --read-timeout | Yes | Read timeout in seconds, recommended value 3 |
SendSms vs SendBatchSms: send-sms shares one signature/template across up to 1000numbers; send-batch-sms lets each phone use a different signature/template params (upto 100 numbers).
aliyun dysmsapi query-send-statistics
| Parameter | Required | Description |
|---|---|---|
| --api-version | Yes | API version, fixed to 2017-05-25 |
| --is-globe | Yes | Scope: 1 = domestic; 2 = international / HK-Macao-Taiwan |
| --start-date | Yes | Start date in yyyyMMdd |
| --end-date | Yes | End date in yyyyMMdd |
| --page-index | Yes | Current page, starts from 1 |
| --page-size | Yes | Page size, 1~50 |
| --template-type | No | 0=verify code, 1=notification, 2=marketing(corp), 3=intl/HK-Macao-Taiwan(corp), 7=digital |
| --sign-name | No | Filter by signature name |
| --user-agent | Yes | Fixed to AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message |
| --read-timeout | Yes | Read timeout in seconds, recommended value 3 |
Response: returnsData.TargetList[](each entry hasSendDate,TotalCount,
RespondedSuccessCount,RespondedFailCount,NoRespondedCount) andData.TotalSize.
Wrapper script options
| Option | Short | Description |
|---|---|---|
| --phone-numbers | -p | Phone numbers |
| --sign-name | -s | Signature name |
| --template-code | -t | Template code |
| --template-param | -tp | Template variables (JSON) |
| --out-id | -o | External tracking ID |
| --verify | — | Verify signature/template before sending |
| --region | -r | Region ID, defaults to cn-hangzhou |
| --profile | — | Use the given aliyun CLI profile |
Output
Successful send response
{
"BizId": "114814474505895421^0",
"Code": "OK",
"Message": "OK",
"RequestId": "4C3D8B1B-B3D8-5673-B724-1F251799CE9A"
}Approval status reference
| Value | SignStatus / TemplateStatus | Description |
|---|---|---|
| 0 | Reviewing | SMS cannot be sent for now |
| 1 | Approved | Available for sending |
| 2 | Rejected | Needs revision and resubmission |
| 10 | Cancelled | Sign/template has been cancelled |
Error Handling
Common business error codes
| Error code | Description | Suggested action |
|---|---|---|
| isv.SMS_SIGNATURE_SCENE_ILLEGAL | Signature/template scene mismatch | Make sure signature and template scenes align |
| isv.SMS_TEMPLATE_ILLEGAL | Template missing or unapproved | Verify the template has been approved |
| isv.SMS_SIGN_NAME_ILLEGAL | Signature missing or unapproved | Verify the signature has been approved |
| isv.MOBILE_NUMBER_ILLEGAL | Bad phone number format | Check the number format |
| isv.AMOUNT_NOT_ENOUGH | Insufficient account balance | Top up the SMS quota |
| isv.BUSINESS_LIMIT_CONTROL | Throttling triggered | Reduce the send rate |
| SignatureNotFound | Signature not found | Create the signature in the console |
| TemplateNotFound | Template not found | Create the template in the console |
query-send-detailsErrCode= Alibabaisv.*/isp.*(above) + carrier receipts (DELIVERED,MOBILE_NOT_ON_SERVICE,MOBILE_SEND_LIMIT, …). Full table + triage: `references/sms-error-codes.md`.
CLI configuration errors
| Error message | Cause | Resolution |
|---|---|---|
| InvalidAccessKeyId.NotFound | Wrong AccessKey ID | Check the AccessKey ID |
| SignatureDoesNotMatch | Wrong Secret | Check the AccessKey Secret |
| Forbidden.RAM | Insufficient permission | Verify the RAM user has SMS permissions |
Execution Flow
1. Pre-check: aliyun CLI installed (>= 3.3.3) and dysmsapi plugin available; AI-Mode enabled; credentials configured. 2. Validate required parameters (phone-numbers, sign-name, template-code). If signature or template is missing, ask the user; after consent call query-sms-sign-list / query-sms-template-list and present approved candidates. 3. [Optional, --verify] Verify sign via get-sms-sign: require SignStatus=1; also surface SignIspRegisterDetailList[*].RegisterResult to the user as info (unregistered carriers may still deliver — do not block). 4. [Optional, --verify] Verify template status via get-sms-template (TemplateStatus=1). 5. Send via aliyun dysmsapi send-sms (or send-batch-sms for per-recipient personalization). 6. Return result: Code=OK only confirms gateway acceptance — return BizId. To report final delivery to the user, always call query-send-details (Scenario 4); never use the send response as proof of delivery. 7. [MUST] Disable AI-Mode at every exit (success / failure / cancellation).
Examples
Scenario 1: Send a verification code
# 1) Pull approved signatures & templates first (so the user can pick)
aliyun dysmsapi query-sms-sign-list --api-version 2017-05-25 --page-index 1 --page-size 50 --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
aliyun dysmsapi query-sms-template-list --api-version 2017-05-25 --page-index 1 --page-size 50 --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# 2) Send (after user confirmation)
aliyun dysmsapi send-sms \
--api-version 2017-05-25 \
--phone-numbers "13800138000" \
--sign-name "YourSign" --template-code "SMS_xxx" \
--template-param '{"code":"123456"}' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Scenario 2: Bulk-send with same signature & params
aliyun dysmsapi send-sms \
--api-version 2017-05-25 \
--phone-numbers "13800138000,13900139000,13700137000" \
--sign-name "YourSign" --template-code "SMS_xxx" \
--template-param '{"activity":"Double 11 Sale","discount":"50% OFF"}' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Scenario 3: Batch send with per-recipient personalization
aliyun dysmsapi send-batch-sms \
--api-version 2017-05-25 \
--phone-number-json '["13800138000","13900139000","13700137000"]' \
--sign-name-json '["BrandA","BrandB","BrandC"]' \
--template-code "SMS_xxx" \
--template-param-json '[{"name":"Alice","code":"111111"},{"name":"Bob","code":"222222"},{"name":"Charlie","code":"333333"}]' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Lengths of--phone-number-json,--sign-name-json, and--template-param-jsonmust
match and correspond one-to-one.
Scenario 4: Verify delivery & aggregate statistics
# Per-message delivery (uses BizId returned by send-sms)
aliyun dysmsapi query-send-details \
--api-version 2017-05-25 \
--phone-number "13800138000" --send-date "20260326" --biz-id "xxx^0" \
--page-size 10 --current-page 1 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# Range-based aggregated statistics (1 row per send-day)
aliyun dysmsapi query-send-statistics \
--api-version 2017-05-25 --is-globe 1 \
--start-date "20260301" --end-date "20260326" \
--page-index 1 --page-size 10 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3QuerySendDetails vs QuerySendStatistics: query-send-details returns per-messagerecords (delivery time, error code) — useful for troubleshooting. query-send-statisticsreturns aggregated daily counts — useful for trend reporting.
Multi-Environment / Multi-Account
aliyun CLI supports multiple profiles. Prefer env vars or RAM roles:
# Option 1: env vars (recommended)
export ALIBABA_CLOUD_ACCESS_KEY_ID=<your-access-key-id>
export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<your-access-key-secret>
# Option 2: profiles (region only; credentials still come from env vars or RAM role)
aliyun configure set --profile prod --region cn-hangzhou
aliyun configure set --profile test --region cn-shanghai
aliyun dysmsapi send-sms --profile prod \
--api-version 2017-05-25 \
--phone-numbers "13800138000" --sign-name "Sign" --template-code "SMS_xxx" \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Notes
1. Plugin install: First-time use requires aliyun plugin install --names dysmsapi. 2. API version: When calling the CLI directly, --api-version 2017-05-25 is mandatory. 3. Parameter format: kebab-case (e.g. --phone-numbers, not --PhoneNumbers). 4. Timeout: --read-timeout 3 is required. After a timeout, query the delivery receipt before deciding whether to retry. 5. User-Agent: --user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message is required so the server can identify the caller. 6. Idempotency: SendSms/SendBatchSms are NOT idempotent — prevent duplicate sends at the business layer. 7. Single vs batch: Prefer send-sms for verification codes; send-batch-sms is only needed when each recipient requires different signature or template params (≤ 100). 8. Billing: Billing depends on carrier delivery; API success but carrier failure is not billed. QPS limit is 5000/s per user. 9. Credential safety: Never hard-code AK/SK; always rely on the default credential chain (env vars / ~/.aliyun/config.json / ECS RAM role / OIDC). 10. Parameter confirmation: Never guess signature/template — always ask the user, and after consent fetch query-sms-sign-list / query-sms-template-list (see "Interactive Parameter Confirmation"). 11. Send-result reporting: send-sms / send-batch-sms responses only confirm gateway acceptance (Code=OK + BizId); they are NOT proof of delivery. Whenever the user asks "did it arrive?" / "was it delivered?", always call query-send-details (use BizId for precise lookup) and surface SendStatus (1=in-flight, 2=failed, 3=delivered) and ErrCode — never reply based on the send-API response alone.
References
- Alibaba Cloud CLI documentation
- SMS Service console
- SendSms API documentation
- Send-status error codes (local) —
query-send-detailsErrCode(Alibabaisv.*/isp.*+ carrier receipts); upstream: 101347 / 101346 - SMS qualification query (local) —
QuerySmsQualificationRecord/QuerySingleSmsQualification
RAM Policies
This Skill calls the Alibaba Cloud Short Message Service (dysmsapi) OpenAPIs and therefore requires the following RAM permissions.
Required Permissions
| Action | Description | Required |
|---|---|---|
dysms:SendSms | Send an SMS | Yes |
dysms:SendBatchSms | Batch send SMS (per-recipient sign/template params, up to 100) | Yes |
dysms:GetSmsSign | Query signature approval status | No |
dysms:GetSmsTemplate | Query template approval status | No |
dysms:QuerySmsSignList | Query the signature list | No |
dysms:QuerySmsTemplateList | Query the template list | No |
dysms:QuerySendDetails | Query SMS send details | No |
dysms:QuerySendStatistics | Query SMS send statistics | No |
dysms:QuerySmsQualificationRecord | List SMS qualification records | No |
dysms:QuerySingleSmsQualification | Get single qualification detail | No |
Minimum Permission Policy
Send SMS only (least privilege). If you also need batch send, add dysms:SendBatchSms to the Action list:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": "dysms:SendSms",
"Resource": "*"
}
]
}Full Permission Policy
Send SMS plus query signatures, templates, and send status:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dysms:SendSms",
"dysms:SendBatchSms",
"dysms:GetSmsSign",
"dysms:GetSmsTemplate",
"dysms:QuerySmsSignList",
"dysms:QuerySmsTemplateList",
"dysms:QuerySendDetails",
"dysms:QuerySendStatistics",
"dysms:QuerySmsQualificationRecord",
"dysms:QuerySingleSmsQualification"
],
"Resource": "*"
}
]
}How to Apply
1. Sign in to the RAM console. 2. Create a custom policy and paste the JSON above. 3. Attach the policy to the appropriate RAM user or role.
SMS Send-Status Error Codes
Reference for the per-message ErrorCode field returned byaliyun dysmsapi query-send-details. Two layers of codes are combined:>
1. Alibaba platform codes (isv.*/isp.*) — surfaced by Alibaba pre-checks
or the SMS gateway before the message is handed off to the carrier.
2. Carrier receipts (DELIVERED,MOBILE_NOT_ON_SERVICE, …) — async
delivery receipts pushed back by domestic carriers (China Mobile / Unicom /
Telecom). Receipt arrival typically takes 10–30 s; absence is common when
SendStatus = 1(sent) but not yetSendStatus = 3(delivered).
>
Upstream source:
Domestic SMS delivery-receipt error codes (101347).
---
How to read query-send-details output
query-send-details returns SmsSendDetailDTOs[], where each entry carries:
| Field | Meaning |
|---|---|
PhoneNum | Recipient mobile number |
SendStatus | 1 = sent (in flight), 2 = failed, 3 = delivered (success) |
ErrCode | The error code documented in this file (only meaningful when SendStatus != 3) |
Content | Rendered SMS body |
SendDate | Submission time (yyyy-MM-dd HH:mm:ss) |
ReceiveDate | Receipt time from carrier (empty when no receipt yet) |
OutId | External tracking ID, if you supplied --out-id on send |
Important — empty `ErrCode` semantics:
SendStatus = 1+ emptyErrCode→ in flight, retry the query later (carrier receipt not arrived yet).SendStatus = 2+ non-emptyErrCode→ failure, look up the code below.SendStatus = 3+ErrCode = DELIVERED(or empty) → success.
---
Section 1 — Alibaba platform codes (isv.* / isp.*)
Returned synchronously at send time and echoed in the receipt. Most can also appear as the response Code of SendSms / SendBatchSms (see SKILL.md Error Handling).
1.1 Account & service-level
| Code | Message | Cause / Resolution |
|---|---|---|
isv.ACCOUNT_NOT_EXISTS | Account not found | Wrong account or AccessKey. Confirm credentials. |
isv.ACCOUNT_ABNORMAL | Account abnormal | Billing query exception. Open a ticket via the SMS DingTalk group. |
isv.OUT_OF_SERVICE | Business stopped (insufficient balance) | Top up the SMS quota. |
isv.PRODUCT_UN_SUBSCRIPT | Cloud-communication product not enabled | Activate the SMS service for the AccessKey owner account. |
isv.PRODUCT_UNSUBSCRIBE | Specific product not subscribed | The AccessKey has not enabled the API's product (e.g. voice). Subscribe first. |
isp.RAM_PERMISSION_DENY | Insufficient RAM permission | Grant AliyunDysmsFullAccess (or scoped) to the RAM user. |
isp.SYSTEM_ERROR | System error | Transient — retry; if persistent, open a support ticket. |
1.2 Signature / template / content
| Code | Message | Cause / Resolution |
|---|---|---|
isv.SMS_SIGN_NAME_ILLEGAL | Signature missing or unapproved | Verify the signature is approved (SignStatus=1). |
isv.SMS_SIGNATURE_ILLEGAL | No matching signature under this account | Check that AK and signature belong to the same account; remove garbled chars. |
isv.SMS_SIGN_ILLEGAL | Signature forbidden | Apply a compliant signature in the console. |
isv.SIGN_STATE_ILLEGAL | Signature state is "unavailable" | Inspect signature detail, fix the unavailability reason and re-submit. |
isv.SMS_SIGNATURE_SCENE_ILLEGAL | Signature/template scene mismatch | Verification-code signatures can only send verification-code templates. |
isv.SMS_TEMPLATE_ILLEGAL | Template missing, unapproved, or vars don't match | Check the template is approved and --template-param keys match the placeholders. |
isv.TEMPLATE_MISSING_PARAMETERS | Template variables not all assigned | Fill every ${...} placeholder in --template-param. |
isv.SMS_CONTENT_ILLEGAL | SMS content contains forbidden words | Revise per the template content guidelines. |
isv.INVALID_PARAMETERS | Parameter format invalid | E.g. SendDate must be yyyyMMdd (20260326, not 2026-03-26). |
isv.INVALID_JSON_PARAM | Parameter is not valid JSON | Use proper JSON: {"code":"123"}. |
isv.EXTEND_CODE_ERROR | Extension code reuse across signatures | Use distinct extension codes per signature. |
1.3 Recipient / quota / control
| Code | Message | Cause / Resolution |
|---|---|---|
isv.MOBILE_NUMBER_ILLEGAL | Phone number format error | Use +86 / 0086 / 86 / bare 11-digit for domestic; international = country-code + number. |
isv.MOBILE_COUNT_OVER_LIMIT | Too many recipients in one call | SendSms ≤ 1000; SendBatchSms ≤ 100. |
isv.BUSINESS_LIMIT_CONTROL | Cloud-communication flow-control hit | Per-recipient frequency cap. Lower send rate or adjust threshold in the console. |
isv.DAY_LIMIT_CONTROL | Daily quota reached | Raise daily threshold in console General Settings → Domestic SMS → Security. |
isv.MONTH_LIMIT_CONTROL | Monthly quota reached | Same as above (monthly). |
isv.BLACK_KEY_CONTROL_LIMIT | Number is on the blacklist | User opted out / complained via 12321; remove from your audience. |
isv.DENY_IP_RANGE | Source IP region blocked | Calls from non-mainland IPs cannot send domestic SMS. |
isv.DOMESTIC_NUMBER_NOT_SUPPORTED | Intl/HK-MO-TW template can't send to mainland | Use a domestic-message template for mainland numbers. |
---
Section 2 — Carrier delivery error codes
Pushed asynchronously by the carrier after the message reaches the destination (or fails to). They populate ErrCode only after SendStatus transitions to 2 (failed) or 3 (delivered).
2.1 Success
| Code | Meaning |
|---|---|
DELIVERED | Carrier confirmed handset reception. Equivalent to SendStatus = 3. |
2.2 Recipient-side failures
| Code | Meaning | Suggested handling |
|---|---|---|
MOBILE_NOT_ON_SERVICE | Number is suspended / out of service / shut down | Stop sending; flag the number invalid in CRM. |
MOBILE_SEND_LIMIT | Recipient hit the per-day per-number receive limit | Defer; do not retry within 24h. |
MOBILE_ACCOUNT_ABNORMAL | Carrier account abnormal (arrears, deregistered, …) | Mark inactive; manual confirmation needed. |
MOBILE_IN_BLACK | Number is on the carrier blacklist | Remove from audience permanently. |
MOBILE_TERMINAL_ERROR | Handset off / out of coverage / not yet activated | Retry once after several hours. |
USER_REJECT | User explicitly refused / has opted-out | Do not retry; honor the opt-out. |
INVALID_NUMBER | Number does not exist on carrier registry | Same as MOBILE_NOT_ON_SERVICE. |
2.3 Content / route-side failures
| Code | Meaning | Suggested handling |
|---|---|---|
CONTENT_KEYWORD | Carrier content audit hit a forbidden keyword | Revise the template; resubmit for approval. |
CONTENT_ERROR | Marketing SMS missing opt-out instruction | Append a regulation-compliant unsubscribe tail (e.g. STOP / reply-T-to-opt-out). |
EXPIRED | Submission expired in carrier queue | Resend with fresh content. |
NO_ROUTE | No usable route to recipient (rare) | Open a ticket with cloud-communication support. |
SP_NOT_BY_INTER_SMS | International sending not supported on this signature/template | Switch to an int'l-enabled signature/template. |
SP_UNKNOWN_ERROR | Carrier returned an opaque error | Retry once; persist failures → ticket. |
REQUEST_SUCCESS | Carrier accepted but final receipt not yet received | Treat as in-flight; query again later. |
The carrier code list above is non-exhaustive — carriers may add or rename
codes without notice. Treat unknown values as transient unless they repeat
across many recipients within a short window (then open a ticket).
---
Section 3 — Triage workflow
Use this decision tree when investigating a failed message:
1. Read `SendStatus` first:
1(in flight) → wait 30 s and re-query; do not retry sending yet.3(delivered) → success path; ignoreErrCode.2(failed) → continue below.
2. Inspect `ErrCode` prefix:
isv.*/isp.*→ look up Section 1.- Account / signature / template issues → fix in console; do not
retry the same payload until the root cause clears.
- Throttling (
BUSINESS_LIMIT_CONTROL/*_LIMIT_CONTROL) → exponential
back-off retry, or raise the threshold in console.
- Otherwise carrier code → look up Section 2.
- Recipient-side failures → mark recipient invalid; do not retry blindly.
- Content / route-side failures → fix content; resend after re-approval.
3. No `ErrCode` but `SendStatus = 2` is rare and indicates an internal gateway issue — open a ticket with the BizId and RequestId.
4. Aggregate analysis: if a single date's RespondedFailCount is unusually high, run query-send-statistics (per signature) to confirm it is not isolated to one signature/template, then sample 20 failed BizIds via query-send-details to identify the dominant ErrCode.
---
Section 4 — Example: enrich a failed delivery report
# 1) For a known phone + date, list per-message details
aliyun dysmsapi query-send-details \
--api-version 2017-05-25 \
--phone-number "13800138000" --send-date "20260326" \
--page-size 50 --current-page 1 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Parse SmsSendDetailDTOs[] and group by ErrCode:
from collections import Counter
import json, sys
data = json.load(sys.stdin)["SmsSendDetailDTOs"]["SmsSendDetailDTO"]
codes = Counter((d["SendStatus"], d.get("ErrCode") or "") for d in data)
for k, v in sorted(codes.items(), key=lambda x: -x[1]):
print(f"status={k[0]} code={k[1]:<35s} count={v}")Cross-reference each ErrCode against Section 1/2 above to choose the right remediation (fix template, revise audience, raise quota, …).
---
References
- Domestic SMS delivery-receipt error codes (101347)
- SMS service error codes (101346)
- QuerySendDetails API
- Local: `SKILL.md` —
## Error Handlingsection; RAM action list is referenced from there.
SMS Qualification Query Reference
This document describes the two dysmsapi qualification query APIs supported by this Skill. Both commands MUST honor the global compliance rules declared in SKILL.md → Critical CLI Compliance (Must Read First):
- aliyun CLI ≥ 3.3.3 with up-to-date plugins
- AI-Mode enabled before invocation, disabled at every exit
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-messageon
every CLI invocation
--read-timeout 3and--api-version 2017-05-25
---
1. QuerySmsQualificationRecord — List Qualification Records
Query the list of SMS qualification records under the current account, including audit details. Supports both full-list query (no filters) and conditional query.
Use this API to discover the GroupId you need before callingQuerySingleSmsQualification for the full detail.CLI command
aliyun dysmsapi query-sms-qualification-record \
--api-version 2017-05-25 \
--page-no 1 --page-size 20 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message \
--read-timeout 3Parameters
| Parameter | Required | Description |
|---|---|---|
| --api-version | Yes | Fixed to 2017-05-25 |
| --qualification-group-name | No | Qualification name filter |
| --company-name | No | Company name filter |
| --state | No | Audit state filter: INIT / NOT_PASS / PASS / NOT_FINISH / CANCEL |
| --work-order-id | No | Audit work-order ID filter |
| --legal-person-name | No | Legal person name filter |
| --use-by-self | No | true = self-use; false = used by others |
| --page-no | No | Page number, starts from 1, defaults to 1 |
| --page-size | No | Page size, range 1~50, defaults to 20 |
| --user-agent | Yes | Fixed to AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message |
| --read-timeout | Yes | Read timeout in seconds, recommended value 3 |
Filter examples
# Only approved (PASS) qualifications
aliyun dysmsapi query-sms-qualification-record \
--api-version 2017-05-25 --state PASS --page-no 1 --page-size 50 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3
# By company name + self-use only
aliyun dysmsapi query-sms-qualification-record \
--api-version 2017-05-25 \
--company-name "Aliyun Communication Co., Ltd." --use-by-self true \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message --read-timeout 3Response shape (key fields)
{
"Code": "OK",
"Message": "OK",
"RequestId": "25D5AFDE-...",
"Success": true,
"Data": {
"PageNo": 1,
"PageSize": 20,
"Total": 25,
"List": [
{
"GroupId": 10000123,
"QualificationGroupName": "Aliyun Communication Co., Ltd. - LiHua",
"CompanyName": "Aliyun Communication Co., Ltd.",
"LegalPersonName": "LiHua",
"WorkOrderId": 20011234,
"StateName": "INIT",
"AuditRemark": "N/A",
"AuditTime": "2024-12-26 17:29:04",
"CreateDate": "2025-02-20 11:59:30",
"UseBySelf": true
}
]
}
}StateName enumeration (list API)
| Value | Meaning |
|---|---|
| INIT | Under review |
| PASS | Approved (available for use) |
| NOT_PASS | Rejected (see AuditRemark) |
| NOT_FINISH | Pending supplement of materials |
| CANCEL | Withdrawn by the user |
IfStateName == NOT_PASS, readAuditRemarkto find out why and call the
"Modify SMS qualification" API or fix it via the SMS console, then re-submit.
---
2. QuerySingleSmsQualification — Single Qualification Detail
Fetch the full detail (company / legal person / admin contact / business license images / etc.) of one qualification by its GroupId. Use this AFTER you have located the target qualification via QuerySmsQualificationRecord.
CLI command
aliyun dysmsapi query-single-sms-qualification \
--api-version 2017-05-25 \
--qualification-group-id 10000123 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message \
--read-timeout 3Parameters
| Parameter | Required | Description |
|---|---|---|
| --api-version | Yes | Fixed to 2017-05-25 |
| --qualification-group-id | Yes | Qualification ID (GroupId returned by query-sms-qualification-record) |
| --order-id | No | Audit work-order ID |
| --user-agent | Yes | Fixed to AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message |
| --read-timeout | Yes | Read timeout in seconds, recommended value 3 |
Response shape (key fields)
{
"Code": "OK",
"Message": "OK",
"RequestId": "25D5AFDE-...",
"Success": true,
"Data": {
"QualificationGroupId": 10000123,
"QualificationName": "Aliyun Communication Co., Ltd. - LiHua",
"CompanyName": "Aliyun Communication Co., Ltd.",
"CompanyType": "COMPANY",
"OrganizationCode": "910X********0012",
"LegalPersonName": "LiHua",
"LegalPersonIDCardType": "identityCard",
"LegalPersonIDCardNo": "511391********5123",
"LegalPersonIdCardEffTime": "2023-01-01~2033-01-01",
"AdminName": "LiHua",
"AdminPhoneNo": "137*******",
"AdminIDCardType": "identityCard",
"AdminIDCardNo": "511391********5123",
"AdminIDCardExpDate": "2023-01-01~2033-01-01",
"AdminIDCardFrontFace": "https://...aliyuncs.com/...",
"AdminIDCardPic": "https://...aliyuncs.com/...",
"BusinessType": "dysms",
"BusinessLicensePics": [
{ "Type": "businessLicense", "LicensePic": "123456/111.png",
"PicUrl": "https://...aliyuncs.com/..." }
],
"OtherFiles": [
{ "LicensePic": "123456/111.png", "PicUrl": "https://...aliyuncs.com/..." }
],
"EffTimeStr": "2023-01-01~2033-01-01",
"UseBySelf": false,
"WhetherShare": false,
"WorkOrderId": 20011234,
"State": "PASSED",
"Remark": "N/A"
}
}State enumeration (single-detail API — note the values differ from list)
| Value | Meaning |
|---|---|
| INT | Under review |
| PASSED | Approved |
| FAILED | Rejected (call list API for remark) |
| NOT_FINISH | Pending supplement |
| CANCELED | Withdrawn |
The single-detail API does NOT return AuditRemark. To get the rejectionreason, fall back to QuerySmsQualificationRecord filtered by--work-order-id of the same record.---
Typical workflow
1. Call query-sms-qualification-record (--state PASS) to locate the target
qualification, take its `GroupId`.
2. Call query-single-sms-qualification --qualification-group-id <GroupId>
to fetch the full company / legal-person / admin / license detail.
3. If the list API shows StateName == NOT_PASS, read `AuditRemark` and either
modify the qualification or open the SMS console to re-submit.Notes
1. Audit windows — qualification audits are processed Mon–Sun 09:00~21:00, typically within 2 working days. Be patient and re-query rather than re-submitting. 2. State naming inconsistency — StateName in the list API and State in the single-detail API use different vocabularies (PASS vs PASSED, INIT vs INT, NOT_PASS vs FAILED, CANCEL vs CANCELED). When correlating results across the two APIs, normalize them at the call-site. 3. PII safety — responses contain ID-card numbers, license images, admin phone numbers. Do NOT log them verbatim or echo them to the user without redaction; mask middle digits when displaying. 4. Pagination — query-sms-qualification-record uses --page-no / --page-size (max 50). Iterate pages until Data.PageNo * Data.PageSize >= Data.Total to walk the full list. 5. RAM permissions — both APIs require the calling RAM identity to hold read permission on dysmsapi. See SKILL.md for the full RAM action list and policy reference.
References
#!/bin/bash
#
# Alibaba Cloud SMS Service CLI helper.
# Wraps the aliyun CLI to send SMS and query related resources.
#
# Requirements: aliyun-cli >= 3.3.3 + dysmsapi plugin
# Install: brew install aliyun-cli (macOS)
# Plugin: aliyun plugin install --names dysmsapi
#
set -e
# Default configuration
DEFAULT_REGION="cn-hangzhou"
API_VERSION="2017-05-25"
USER_AGENT="AlibabaCloud-Agent-Skills/alibabacloud-sms-send-short-message"
READ_TIMEOUT="3"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# ========== Input validators ==========
# Validate phone numbers (only digits, commas and plus signs allowed)
validate_phone_numbers() {
local phone="$1"
if [[ ! "$phone" =~ ^[0-9,+]+$ ]]; then
echo -e "${RED}Error: invalid phone number format. Only digits, commas and '+' are allowed.${NC}"
exit 1
fi
}
# Validate signature name (forbid shell metacharacters)
validate_sign_name() {
local sign="$1"
# Forbid shell metacharacters: $ ` \ " ' ; & | > < ( ) { } [ ] ! # * ?
if [[ "$sign" =~ [\$\`\\\"\'\'\;\&\|\>\<\(\)\{\}\[\]\!\#\*\?] ]]; then
echo -e "${RED}Error: signature name contains illegal characters.${NC}"
exit 1
fi
}
# Validate template code (only letters, digits and underscore)
validate_template_code() {
local code="$1"
if [[ ! "$code" =~ ^[A-Za-z0-9_]+$ ]]; then
echo -e "${RED}Error: invalid template code. Only letters, digits and underscore are allowed.${NC}"
exit 1
fi
}
# Validate date format (digits only, yyyyMMdd)
validate_date() {
local date="$1"
if [[ ! "$date" =~ ^[0-9]{8}$ ]]; then
echo -e "${RED}Error: invalid date format. Expected yyyyMMdd.${NC}"
exit 1
fi
}
# Validate JSON parameter (basic check; forbid dangerous characters)
validate_json_param() {
local json="$1"
# Forbid shell metacharacters except those required by JSON: " : , { } [ ]
if [[ "$json" =~ [\$\`\\\;\&\|\>\<\(\)\!\#\*\?] ]]; then
echo -e "${RED}Error: JSON parameter contains illegal characters.${NC}"
exit 1
fi
}
# Validate a generic safe string (forbid dangerous characters)
validate_safe_string() {
local str="$1"
local name="$2"
if [[ "$str" =~ [\$\`\\\"\'\'\;\&\|\>\<\(\)\{\}\[\]\!\#\*\?] ]]; then
echo -e "${RED}Error: ${name} contains illegal characters.${NC}"
exit 1
fi
}
# Help message
show_help() {
cat << EOF
Alibaba Cloud SMS Service CLI helper
Usage:
$0 <command> [options]
Commands:
send Send an SMS message
list-signs List SMS signatures
list-templates List SMS templates
query-sign Query a specific signature status
query-template Query a specific template status
query-status Query the send status of an SMS
query-statistics Query send statistics by date range
Send options:
-p, --phone-numbers Phone numbers (required), comma-separated for multiple
-s, --sign-name Signature name (required)
-t, --template-code Template code (required)
-tp, --template-param Template variables (JSON)
-o, --out-id External tracking ID
--verify Verify signature & template status before sending
Query options:
--sign-name Signature name (required for query-sign)
--template-code Template code (required for query-template)
--send-date Send date in yyyyMMdd (required for query-status)
--biz-id BizId returned by send
Statistics options (query-statistics):
--start-date Start date in yyyyMMdd (required)
--end-date End date in yyyyMMdd (required)
--is-globe Scope: 1 = domestic, 2 = international/HK-Macao-Taiwan (default 1)
--template-type Template type: 0=verification, 1=notification, 2=marketing,
3=international, 7=digital
--page-index Page index, default 1
--page-size Page size, default 10 (1-50)
Common options:
-r, --region Region ID, default cn-hangzhou
--profile Use the given aliyun CLI profile
-h, --help Show this help
Examples:
# Send an SMS
$0 send -p "13800138000" -s "AliyunDemo" -t "SMS_123456" -tp '{"code":"123456"}'
# Verify signature/template first
$0 send -p "13800138000" -s "AliyunDemo" -t "SMS_123456" -tp '{"code":"123456"}' --verify
# List signatures
$0 list-signs
# List templates
$0 list-templates
# Query a signature status
$0 query-sign --sign-name "AliyunDemo"
# Query a template status
$0 query-template --template-code "SMS_123456"
# Query send status
$0 query-status -p "13800138000" --send-date "20260326"
# Query send statistics
$0 query-statistics --start-date "20260301" --end-date "20260326"
EOF
}
# Check whether the aliyun CLI is installed
check_cli() {
if ! command -v aliyun &> /dev/null; then
echo -e "${RED}Error: aliyun CLI is not installed.${NC}"
echo "Please install aliyun CLI first:"
echo " macOS: brew install aliyun-cli"
echo " Linux: see https://help.aliyun.com/zh/cli/"
exit 1
fi
}
# Check whether the dysmsapi plugin is installed
check_plugin() {
if ! aliyun dysmsapi --help &> /dev/null; then
echo -e "${YELLOW}Installing the dysmsapi plugin...${NC}"
aliyun plugin install --names dysmsapi
if [ $? -ne 0 ]; then
echo -e "${RED}Error: failed to install the dysmsapi plugin.${NC}"
echo "Install it manually with: aliyun plugin install --names dysmsapi"
exit 1
fi
echo -e "${GREEN}dysmsapi plugin installed successfully.${NC}"
fi
}
# Check the CLI configuration
check_config() {
# Verify that credentials are configured (environment variable or config file)
if [ -z "$ALIBABA_CLOUD_ACCESS_KEY_ID" ] && ! aliyun configure get 2>/dev/null | grep -q "access_key_id"; then
echo -e "${YELLOW}Warning: aliyun CLI has no credentials configured.${NC}"
echo "Configure credentials in one of the following ways:"
echo " 1. Environment variables: export ALIBABA_CLOUD_ACCESS_KEY_ID=xxx && export ALIBABA_CLOUD_ACCESS_KEY_SECRET=xxx"
echo " 2. ECS instance RAM role (auto-detected on cloud workloads)"
echo " 3. OIDC token (container workloads)"
exit 1
fi
}
# Send an SMS
send_sms() {
local phone_numbers=""
local sign_name=""
local template_code=""
local template_param=""
local out_id=""
local verify=false
while [[ $# -gt 0 ]]; do
case $1 in
-p|--phone-numbers)
phone_numbers="$2"
shift 2
;;
-s|--sign-name)
sign_name="$2"
shift 2
;;
-t|--template-code)
template_code="$2"
shift 2
;;
-tp|--template-param)
template_param="$2"
shift 2
;;
-o|--out-id)
out_id="$2"
shift 2
;;
--verify)
verify=true
shift
;;
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
# Validate required parameters
if [ -z "$phone_numbers" ]; then
echo -e "${RED}Error: missing phone numbers (-p/--phone-numbers).${NC}"
exit 1
fi
if [ -z "$sign_name" ]; then
echo -e "${RED}Error: missing signature name (-s/--sign-name).${NC}"
exit 1
fi
if [ -z "$template_code" ]; then
echo -e "${RED}Error: missing template code (-t/--template-code).${NC}"
exit 1
fi
# Input validation (defends against command injection)
validate_phone_numbers "$phone_numbers"
validate_sign_name "$sign_name"
validate_template_code "$template_code"
[ -n "$template_param" ] && validate_json_param "$template_param"
[ -n "$out_id" ] && validate_safe_string "$out_id" "out-id"
# Pre-send verification
if [ "$verify" = true ]; then
echo -e "${YELLOW}Verifying signature status...${NC}"
local sign_result
sign_result=$(aliyun dysmsapi get-sms-sign \
--api-version "$API_VERSION" \
--user-agent "$USER_AGENT" \
--read-timeout "$READ_TIMEOUT" \
${REGION:+--region "$REGION"} \
${PROFILE:+--profile "$PROFILE"} \
--sign-name "$sign_name" 2>&1)
local sign_status
sign_status=$(echo "$sign_result" | grep -o '"SignStatus":[0-9]*' | grep -o '[0-9]*')
if [ "$sign_status" != "1" ]; then
echo -e "${RED}Signature verification failed: not approved (status: $sign_status).${NC}"
echo "$sign_result"
exit 1
fi
echo -e "${GREEN}Signature verified.${NC}"
echo -e "${YELLOW}Verifying template status...${NC}"
local template_result
template_result=$(aliyun dysmsapi get-sms-template \
--api-version "$API_VERSION" \
--user-agent "$USER_AGENT" \
--read-timeout "$READ_TIMEOUT" \
${REGION:+--region "$REGION"} \
${PROFILE:+--profile "$PROFILE"} \
--template-code "$template_code" 2>&1)
local template_status
template_status=$(echo "$template_result" | grep -o '"TemplateStatus":[0-9]*' | grep -o '[0-9]*')
if [ "$template_status" != "1" ]; then
echo -e "${RED}Template verification failed: not approved (status: $template_status).${NC}"
echo "$template_result"
exit 1
fi
echo -e "${GREEN}Template verified.${NC}"
fi
# Build the command via an array (avoid eval)
local cmd_args=(
"aliyun" "dysmsapi" "send-sms"
"--api-version" "$API_VERSION"
"--user-agent" "$USER_AGENT"
"--read-timeout" "$READ_TIMEOUT"
"--phone-numbers" "$phone_numbers"
"--sign-name" "$sign_name"
"--template-code" "$template_code"
)
[ -n "$template_param" ] && cmd_args+=("--template-param" "$template_param")
[ -n "$out_id" ] && cmd_args+=("--out-id" "$out_id")
[ -n "$REGION" ] && cmd_args+=("--region" "$REGION")
[ -n "$PROFILE" ] && cmd_args+=("--profile" "$PROFILE")
echo -e "${YELLOW}Sending SMS...${NC}"
"${cmd_args[@]}"
}
# List signatures
list_signs() {
while [[ $# -gt 0 ]]; do
case $1 in
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
echo -e "${YELLOW}Querying signature list...${NC}"
aliyun dysmsapi query-sms-sign-list \
--api-version "$API_VERSION" \
--user-agent "$USER_AGENT" \
--read-timeout "$READ_TIMEOUT" \
${REGION:+--region "$REGION"} \
${PROFILE:+--profile "$PROFILE"} \
--page-index 1 \
--page-size 50
}
# List templates
list_templates() {
while [[ $# -gt 0 ]]; do
case $1 in
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
echo -e "${YELLOW}Querying template list...${NC}"
aliyun dysmsapi query-sms-template-list \
--api-version "$API_VERSION" \
--user-agent "$USER_AGENT" \
--read-timeout "$READ_TIMEOUT" \
${REGION:+--region "$REGION"} \
${PROFILE:+--profile "$PROFILE"} \
--page-index 1 \
--page-size 50
}
# Query a specific signature status
query_sign() {
local sign_name=""
while [[ $# -gt 0 ]]; do
case $1 in
--sign-name|-s)
sign_name="$2"
shift 2
;;
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -z "$sign_name" ]; then
echo -e "${RED}Error: missing signature name (--sign-name).${NC}"
exit 1
fi
# Input validation
validate_sign_name "$sign_name"
echo -e "${YELLOW}Querying signature status...${NC}"
aliyun dysmsapi get-sms-sign \
--api-version "$API_VERSION" \
--user-agent "$USER_AGENT" \
--read-timeout "$READ_TIMEOUT" \
${REGION:+--region "$REGION"} \
${PROFILE:+--profile "$PROFILE"} \
--sign-name "$sign_name"
}
# Query a specific template status
query_template() {
local template_code=""
while [[ $# -gt 0 ]]; do
case $1 in
--template-code|-t)
template_code="$2"
shift 2
;;
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -z "$template_code" ]; then
echo -e "${RED}Error: missing template code (--template-code).${NC}"
exit 1
fi
# Input validation
validate_template_code "$template_code"
echo -e "${YELLOW}Querying template status...${NC}"
aliyun dysmsapi get-sms-template \
--api-version "$API_VERSION" \
--user-agent "$USER_AGENT" \
--read-timeout "$READ_TIMEOUT" \
${REGION:+--region "$REGION"} \
${PROFILE:+--profile "$PROFILE"} \
--template-code "$template_code"
}
# Query the send status
query_status() {
local phone_number=""
local send_date=""
local biz_id=""
while [[ $# -gt 0 ]]; do
case $1 in
-p|--phone-numbers)
phone_number="$2"
shift 2
;;
--send-date)
send_date="$2"
shift 2
;;
--biz-id)
biz_id="$2"
shift 2
;;
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -z "$phone_number" ]; then
echo -e "${RED}Error: missing phone number (-p/--phone-numbers).${NC}"
exit 1
fi
if [ -z "$send_date" ]; then
# Default to today
send_date=$(date +%Y%m%d)
echo -e "${YELLOW}--send-date not provided, defaulting to today: $send_date${NC}"
fi
# Input validation
validate_phone_numbers "$phone_number"
validate_date "$send_date"
[ -n "$biz_id" ] && validate_safe_string "$biz_id" "BizId"
# Build the command via an array (avoid eval)
local cmd_args=(
"aliyun" "dysmsapi" "query-send-details"
"--api-version" "$API_VERSION"
"--user-agent" "$USER_AGENT"
"--read-timeout" "$READ_TIMEOUT"
"--phone-number" "$phone_number"
"--send-date" "$send_date"
"--page-size" "10"
"--current-page" "1"
)
[ -n "$biz_id" ] && cmd_args+=("--biz-id" "$biz_id")
[ -n "$REGION" ] && cmd_args+=("--region" "$REGION")
[ -n "$PROFILE" ] && cmd_args+=("--profile" "$PROFILE")
echo -e "${YELLOW}Querying send status...${NC}"
"${cmd_args[@]}"
}
# Query send statistics
query_statistics() {
local start_date=""
local end_date=""
local is_globe="1"
local template_type=""
local sign_name=""
local page_index="1"
local page_size="10"
while [[ $# -gt 0 ]]; do
case $1 in
--start-date)
start_date="$2"
shift 2
;;
--end-date)
end_date="$2"
shift 2
;;
--is-globe)
is_globe="$2"
shift 2
;;
--template-type)
template_type="$2"
shift 2
;;
--sign-name|-s)
sign_name="$2"
shift 2
;;
--page-index)
page_index="$2"
shift 2
;;
--page-size)
page_size="$2"
shift 2
;;
-r|--region)
REGION="$2"
shift 2
;;
--profile)
PROFILE="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -z "$start_date" ]; then
echo -e "${RED}Error: missing start date (--start-date).${NC}"
exit 1
fi
if [ -z "$end_date" ]; then
echo -e "${RED}Error: missing end date (--end-date).${NC}"
exit 1
fi
# Input validation
validate_date "$start_date"
validate_date "$end_date"
if [[ ! "$is_globe" =~ ^[12]$ ]]; then
echo -e "${RED}Error: --is-globe must be 1 (domestic) or 2 (international/HK-Macao-Taiwan).${NC}"
exit 1
fi
if [ -n "$template_type" ] && [[ ! "$template_type" =~ ^[01237]$ ]]; then
echo -e "${RED}Error: --template-type must be one of 0/1/2/3/7.${NC}"
exit 1
fi
if [[ ! "$page_index" =~ ^[0-9]+$ ]] || [[ ! "$page_size" =~ ^[0-9]+$ ]]; then
echo -e "${RED}Error: --page-index / --page-size must be numbers.${NC}"
exit 1
fi
[ -n "$sign_name" ] && validate_sign_name "$sign_name"
# Build the command via an array (avoid eval)
local cmd_args=(
"aliyun" "dysmsapi" "query-send-statistics"
"--api-version" "$API_VERSION"
"--user-agent" "$USER_AGENT"
"--read-timeout" "$READ_TIMEOUT"
"--is-globe" "$is_globe"
"--start-date" "$start_date"
"--end-date" "$end_date"
"--page-index" "$page_index"
"--page-size" "$page_size"
)
[ -n "$template_type" ] && cmd_args+=("--template-type" "$template_type")
[ -n "$sign_name" ] && cmd_args+=("--sign-name" "$sign_name")
[ -n "$REGION" ] && cmd_args+=("--region" "$REGION")
[ -n "$PROFILE" ] && cmd_args+=("--profile" "$PROFILE")
echo -e "${YELLOW}Querying send statistics...${NC}"
"${cmd_args[@]}"
}
# Main entry
main() {
# Dependency checks
check_cli
check_plugin
check_config
# Initialize variables
REGION="${DEFAULT_REGION}"
PROFILE=""
# Parse the command
if [ $# -eq 0 ]; then
show_help
exit 0
fi
local command=$1
shift
case $command in
send)
send_sms "$@"
;;
list-signs)
list_signs "$@"
;;
list-templates)
list_templates "$@"
;;
query-sign)
query_sign "$@"
;;
query-template)
query_template "$@"
;;
query-status)
query_status "$@"
;;
query-statistics)
query_statistics "$@"
;;
-h|--help|help)
show_help
;;
*)
echo -e "${RED}Unknown command: $command${NC}"
echo "Run '$0 --help' for usage information."
exit 1
;;
esac
}
main "$@"