CRITICAL: Before starting, MUST first use the Read tool to read
../openydt-shared/SKILL.md(authentication / profile / signature / status codes / rate limiting / security rules).openydt apiIt follows exactly the same signature, envelope, exit code, and rate limiting logic as first-class commands; do not execute any command without reading the shared foundation.
When to use this skill
The platform has many interfaces, among whichmost have been made into domain first-class commands(openydt <域> <命令>, with parameters already structured as flags), but there are still quite a few that have not been made first-class (for authoritative counts see make counts / INTERFACE_INDEX.md, do not memorize numbers). When the interface you want to callhas no dedicated subcommand, use this skill's generic fallback:
openydt api <cmd> --body '{...}'
api forany callable (direction=callable) business code cmd automatically signs and POST, covering all domains not named by first-class commands, for example:
cityOperationCouponcity operation coupons (create/issue city operation coupon templates)thirdParkForBolianthird-party parking lot payment access receiptupwarduplink data reporting receipt (such asasynSuccess)communitycommunity access control (such asgetAuthCommunities)adad statistics,preferential/scorepoints redemption,invoiceinvoices,ydtUseruser authentication, etc.- interfaces that have a dedicated domain but whose sub-features have not been made first-class: monthly pass reservation/queueing (
ticket·appointment), monthly pass authorized visitors (ticket·authorize), monthly pass document rules (ticket·certificate), vehicle tags (parking·tag:addCarTags/delCarTags) — the monthticket / record domain skills will point these to this skill, also useapito call (remember for write operations--yes)
These domains in catalog.json have included=false(no first-class command generated), but as long as direction=callable you can use api to call directly.
Selection order:first look for a first-class command(
openydt <域> --helpor the corresponding domain skill), if not found then useapifallback. First-class commands split parameters into flags, automatically determine read/write, and are less error-prone;apiis "raw JSON direct send", more generic but you must ensure the body is correct yourself.
Usage: openydt api
# 1) 行内 JSON body(最常用)
openydt api getParkFee --body '{"parkCode":"1ZS7H5PQH9","carCode":"粤EJW962"}'
# 2) 无参接口可省略 body
openydt api getAuthParkCodes
# 3) 从文件读 body
openydt api getParkOnSiteCar --body-file ./body.json
# 4) 从 stdin 读 body(- 表示 stdin),适合管道 / 大 body
echo '{"parkCode":"PTD2YBBZ"}' | openydt api getParkOnSiteCar --body-file -
<cmd>: business code,is thecmdfield in the catalog(such asgetParkFee、createCityOperationCouponTemplate). Note it is cmd, notdirpath.--bodyand--body-fileare mutually exclusive; if neither is given, an empty body is sent ({}), only suitable for interfaces without parameters.- body israw JSON: the CLI will first do JSON compact before using it for signing and sending (consistent with first-class commands), spaces inside strings such as
"2019-04-16 00:11:25"will be preserved. - Parameter naming and nested structurecompletely copy the interface definition(see below for looking up parameters from the catalog); writing the wrong field name usually returns
status=2 / resultCode=909 请求参数错误orstatus=7 请求参数不完整。
--dry-run preview
If unsure about the body or before the prod environment, first use --dry-run to only printthe signed request to be sent(URL / sign / ts / the compacted body), not actually sent:
openydt api createCityOperationCouponTemplate --dry-run \
--body '{"parkCodeList":["1ZS7H5PQH9"],"couponTemplate":{"name":"抵扣1元券","faceValue":1}}'
The example parkCode/time are documented test values (test only); copying the catalog's historical sampleBody will hit an invalid parking lot/expired validity period.
Write operations must use --yes (api and first-class commands share the write guard)
api and first-class commands now go throughthe same write guard——RunCall for catalog readwrite=write 's cmd, uniformly require --yes(or --dry-run):
- omitting
--yeswill be blocked, it won't actually be sent; the system will prompt "This is a write operation, add --yes to confirm". - Global
--read-only(or the environment variableOPENYDT_READ_ONLY=1), any write cmd (includingapi) is rejected unconditionally. - For any cmd that changes platform state (create/modify/delete, issue coupon, pay, open gate, report receipt, etc.), stillrecommend first using
--dry-runto preview the signed request, confirm it's correct, then--yesactually send—double assurance, especially important in prod. - To determine whether a cmd is read or write: look at the catalog's
readwritefield (see "Querying from the catalog" below). For idempotency/retry of write operations, see [[openydt-shared]]'sreferences/write-idempotency.md。
# 写操作(catalog readwrite=write),必须先 --dry-run 预览,再 --yes 实发
openydt api createCityOperationCouponTemplate --dry-run \
--body '{"parkCodeList":["1ZS7H5PQH9"],"couponTemplate":{"name":"抵扣1元券","totalNum":2,"couponType":1,"faceValue":1,"validFrom":"2026-06-01 00:00:00","validTo":"2027-06-01 00:00:00"}}'
openydt api createCityOperationCouponTemplate --yes \
--body '{"parkCodeList":["1ZS7H5PQH9"],"couponTemplate":{"name":"抵扣1元券","totalNum":2,"couponType":1,"faceValue":1,"validFrom":"2026-06-01 00:00:00","validTo":"2027-06-01 00:00:00"}}'
Query the catalog for available cmds and parameters
The interface list is in ../../catalog/catalog.json(absolute path /Users/zhoujw/develop/tmp/openydt-cli/catalog/catalog.json). Top-level structure {generatedFrom, count, interfaces:[...]}, key fields of each interface object:
| Field | Meaning |
|---|---|
cmd |
Business code,directly used as openydt api <cmd> 's cmd |
domain / dir |
Owning domain / doc path (for categorization only,not an input parameter of api) |
direction |
callable= can be actively called / webhook= actively pushed by the platform (see next section,cannot be called) |
readwrite |
read / write—determines whether to add --yes |
included |
Whether it has been made into a first-class command;false means to use api as a fallback |
excludeReason |
The reason it hasn't been made into a first-class command (out-of-scope-domain / deprecated / no-endpoint / vems-only etc.) |
params |
Parameter definition array:name / required / type / desc / group(group being nonempty means the parameter is nested inside some sub-object) |
sampleBody |
Official example request body—the best starting point for constructing --body , copy the field names then change the values |
sampleResponse |
Example response, to help you anticipate the returned fields |
Use jq / python3 to search (don't print the secret in the terminal; here we only query the list, no credentials involved):
# 按 cmd 看完整定义(params + sampleBody)
jq '.interfaces[] | select(.cmd=="createCityOperationCouponTemplate")' catalog/catalog.json
# 列出某个未点名域所有“可调用”的 cmd 及读写
jq -r '.interfaces[] | select(.domain=="cityOperationCoupon" and .direction=="callable") | "\(.cmd)\t\(.readwrite)\t\(.explain)"' catalog/catalog.json
# 全量“没做成一等命令但可调用”的接口(included=false 且 callable)
jq -r '.interfaces[] | select(.included==false and .direction=="callable") | "\(.domain)\t\(.cmd)\t\(.readwrite)"' catalog/catalog.json
# 只看某 cmd 的参数清单(含嵌套 group)
jq '.interfaces[] | select(.cmd=="createCityOperationCouponTemplate") | .params' catalog/catalog.json
Interfaces with included=false but direction=callable can still be called with api —common in city operation coupons, third-party parking lot access (thirdParkForBolian), uplink receipts (upward), community access control (community), ads/points/invoices/ydtUser and other "unnamed domains".—common in city operation coupons, third-party parking lot access (thirdParkForBolian), uplink receipts (upward), community access control (community), ads/points/invoices/ydtUser and other "unnamed domains".included=false only means "no dedicated command was generated"; it doesn't mean "cannot be called"; to judge whether it can be called, look at direction, and to judge whether --yes is needed, look at readwrite。
Construction flow: ① jq get the target cmd's sampleBody → ② copy the field names, check required/type/nesting per params → ③ --dry-run preview the signed request → ④ for write operations add --yes to formally send.
Quick error self-healing reference (common with api fallback)
| Symptom | Meaning | Recovery action |
|---|---|---|
status=9 接口不存在 |
cmd misspelled / this cmd is a webhook (cannot be called proactively) | `jq '.interfaces[] |
status=2 resultCode=909 / status=7 |
body field name/required field error | According to the catalog, this cmd's params check required fields and nested groupitem by item, start with sampleBody start with |
writing cmd missing --yes blocked with "this is a write operation, add --yes" |
RunCall write guard (shared by api and first-class commands) | First --dry-run preview, confirm it is a write cmd then --yes |
For common codes, exit codes, and retry semantics see [[openydt-shared]]; for idempotency keys see its
references/write-idempotency.md。
webhooks that cannot be called (platform-initiated push)
In the catalog, direction=webhook interfaces (such as reportParkinglotChange parking space change report; for the count see make counts) areplatform-initiated POST to your receivercallbacks, with the direction opposite to callable:
- the CLI cannot proactively call these cmds—they have no entry point for "your request to the platform",
openydt api <webhook-cmd>is not correct usage (the platform does not provide an endpoint in that direction). - To receive such pushes, you need tobuild your own HTTP receiver (webhook receiver), register the callback address with the platform, and the platform will POST data in the form of
sampleBodyto you when the event occurs; your service is responsible for signature verification, processing, and returning as agreed (many uplink receipts correspond to a callable confirmation cmd in theupwarddomain, such asasynSuccess)。 - How to distinguish: before calling, first
jq '.interfaces[]|select(.cmd=="<cmd>")|.direction', if it iswebhookthen do not useapito call it; instead build your own receiver; only if it iscallableshould you useapi。
Example
Check which callable cmds exist in an unnamed domain (read, purely queries the catalog, sends no request):
jq -r '.interfaces[] | select(.domain=="thirdParkForBolian" and .direction=="callable") | "\(.cmd)\t\(.readwrite)"' catalog/catalog.json
Start with sampleBody, preview first then send (write operation example; for a formal send add --yes):
# 1) 取示例 body
jq -r '.interfaces[]|select(.cmd=="createCityOperationCouponTemplate")|.sampleBody' catalog/catalog.json
# 2) 预览签名请求,确认无误
openydt api createCityOperationCouponTemplate --dry-run --body-file ./body.json
# 3) 正式发(写操作,必须 --yes)
openydt api createCityOperationCouponTemplate --yes --body-file ./body.json
Read interfaces can be called directly (no --yes needed):
echo '{}' | openydt api getAuthCommunities --body-file - # community 域,readwrite=read