API v1 · Xưởng mã thiết bị
Kết nối Xưởng với ứng dụng của bạn
Thêm tài khoản, hoàn tất xác thực và lấy mã sáu số qua API. Tài khoản và tiến trình tạo bằng API cũng xuất hiện trên bảng điều khiển của bạn.
Base URLhttps://otp.thanhnguyen.sbs/api/v1/workshop
- Tạo API Key tại màn hình API KEY.
-
Gửi tài khoản để nhận
account.idvàjob.id. - Theo dõi tiến trình, gửi OTP nếu được yêu cầu.
-
Khi hoàn tất, lấy
device_apihoặc gọi API lấy mã.
Chọn ngôn ngữ thông báo bằng Accept-Language: vi, en hoặc zh-CN. X-XID-Language được ưu tiên hơn cookie ngôn ngữ; cookie được ưu tiên hơn Accept-Language. Mặc định API là tiếng Việt. Tên trường, HTTP status, mã error/status, ID, URL và dữ liệu người dùng không đổi.
API Key và quyền truy cập
Key đầy đủ chỉ hiện một lần khi tạo. Mỗi user được có tối đa 10 key hoạt động, với thời hạn và quyền riêng. Bạn có thể thu hồi bất cứ lúc nào trên màn hình API KEY.
Authorization: Bearer xid_KEY_CUA_BAN
Content-Type: application/json
-
workshop:read: xem tài khoản, hạn mức và tiến trình. -
workshop:write: thêm/đăng nhập, xác thực, tạm dừng, quản lý thư mục/ghi chú, gỡ khỏi danh sách và hủy tiến trình. -
workshop:code: lấy mã và xem liên kết lấy mã. Đổi liên kết cần cả quyền write và code.
Mỗi key chỉ có quyền trên tài khoản của chủ key, dùng chung hạn mức
với web. Key hết hạn, bị thu hồi hoặc bị vô hiệu hóa sau đổi mật
khẩu/khóa user sẽ trả 401.
Gọi API từ chương trình hoặc backend của bạn. Giữ key trên server;
không nhúng vào JavaScript công khai hoặc query URL. Mọi yêu cầu có
body, kể cả thao tác dùng {}, cần Content-Type JSON.
BASE="https://otp.thanhnguyen.sbs/api/v1/workshop"
# XID_API_KEY là biến môi trường chứa key của bạn.
curl --fail-with-body "$BASE/me" \
-H "Authorization: Bearer $XID_API_KEY"
Thêm tài khoản và bắt đầu đăng nhập
Tạo file account.json trên máy của bạn với nội dung
dưới đây. Các trường label, note,
folder_id, proxy và otp_source
đều tùy chọn. Xóa file chứa mật khẩu sau khi dùng.
{
"apple_id": "your-account@example.com",
"password": "MAT_KHAU_CUA_BAN",
"label": "Tài khoản của tôi",
"note": "Nhóm khách hàng A"
}
Mẫu này dùng cấu hình proxy đã lưu của bạn. Xem Cấu hình proxy để chọn proxy hoặc tắt proxy.
# Mỗi thao tác mới có một UUID mới. Giữ lại để gửi lại cùng yêu cầu.
REQUEST_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
curl --fail-with-body "$BASE/accounts" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $REQUEST_ID" \
--data-binary @account.json
Phản hồi 202 có dạng
{"account": {...}, "job": {...}}. Lưu cả hai ID;
202 chỉ xác nhận đã nhận yêu cầu, chưa có nghĩa đăng
nhập thành công.
JOB_ID="ID_TIEN_TRINH"
curl --fail-with-body "$BASE/jobs/$JOB_ID" \
-H "Authorization: Bearer $XID_API_KEY"
Kiểm tra mỗi 3 giây. Trạng thái tiến trình: queued,
processing, awaiting_otp,
succeeded, failed hoặc
cancelled.
Link nhận OTP tự động
Thêm otp_source vào yêu cầu tạo/đăng nhập để đọc
mã từ nguồn SMS thuộc quyền sử dụng của bạn.
"otp_source": {
"url": "https://sms.example/your-private-endpoint",
"phone": "+14092986901"
}
phone giúp chọn đúng số tin cậy. Nếu
không chỉ định số và chỉ có một số tin cậy, hệ thống chọn số duy
nhất. Xem tài liệu đầy đủ để biết các định dạng.
Cấu hình proxy qua API
Gửi proxy trong body
POST /accounts hoặc
POST /accounts/{id}/login. Proxy chỉ áp dụng cho
kết nối từ dịch vụ tới Apple khi đăng nhập, gồm chọn
cách nhận mã, gửi mã và nhập OTP. Proxy đã chọn được giữ cố định
trong cùng job; không áp dụng cho đăng nhập website, kết nối từ
ứng dụng của bạn tới API hoặc lấy mã qua /code và
device_api.
-
Bỏ
proxyhoặc gửi"proxy": null: dùng cấu hình proxy đã lưu của chủ API Key. Nếu chưa lưu, mặc định không dùng proxy. -
Gửi đối tượng
proxy: cập nhật cấu hình đã lưu của user, dùng chung với web và các lần tạo/đăng nhập tiếp theo. Job đang chạy giữ cấu hình đã nhận. -
"proxy": {"mode":"none"}tắt proxy trong cấu hình đã lưu và cho job mới.directlà dùng một proxy cụ thể;nonelà kết nối không qua proxy.
Thêm một trong các trường sau vào file account.json
rồi gửi bằng lệnh cURL ở trên. Các tên miền .invalid
chỉ là ví dụ; thay bằng địa chỉ của bạn. Khi đăng nhập lại,
body cần password và trường proxy đã chọn.
Một proxy: direct
{
"proxy": {
"mode": "direct",
"strategy": "per_account",
"value": "http://PROXY_USER:PROXY_PASSWORD@proxy.example.invalid:8080"
}
}
API cấp proxy: api
{
"proxy": {
"mode": "api",
"strategy": "smart",
"value": "https://proxy-provider.example.invalid/next?key=YOUR_PROVIDER_KEY"
}
}
Danh sách proxy: list
{
"proxy": {
"mode": "list",
"strategy": "per_account",
"value": "http://proxy-1.example.invalid:8080\nsocks5://PROXY_USER:PROXY_PASSWORD@proxy-2.example.invalid:1080"
}
}
value là chuỗi, mỗi dòng một proxy; trong JSON viết
xuống dòng bằng \n.
Tắt proxy: none
{"proxy": {"mode": "none"}}
strategy mặc định là per_account.
Trong Xưởng, chiến lược này tái sử dụng phân bổ đã lưu theo tài khoản;
nguồn list/api có thể phục vụ nhiều tài khoản
qua các lượt dùng chung. smart ưu tiên proxy khả dụng đang rảnh hoặc có ít lượt chờ nhất,
tái sử dụng proxy khỏe cho lượt mới. Cả hai giữ proxy cố định
sau khi phiên bắt đầu, kể cả qua OTP; với
direct, mọi tài khoản dùng địa chỉ duy nhất đã nhập.
Hỗ trợ http, https, socks4,
socks5, socks5h và cổng cụ thể. Proxy phải
có địa chỉ công khai; URL nhà cung cấp ở chế độ api
phải dùng HTTPS. Khi giữ nguyên mode, bỏ value hoặc
gửi null/chuỗi trống sẽ dùng lại giá trị đã lưu của mode đó, nếu có.
Account/job không trả thông tin xác thực proxy. Giữ file chứa proxy có mật khẩu hoặc URL có key riêng tư và không in ra log. Mẫu Python phía dưới dùng prompt ẩn cho proxy.
Chọn nhận mã trên thiết bị hoặc qua SMS
Khi job.status = awaiting_otp, có thể nhập mã trên
thiết bị tin cậy. Để chọn số điện thoại, gọi API tải lựa chọn rồi
đọc lại tiến trình đến khi
two_factor.options_loading = false và
options_loaded = true. Nếu có
options_error, chưa chọn số; hãy kiểm tra tiến trình và
thử tải lại sau.
curl --fail-with-body "$BASE/jobs/$JOB_ID/factor-options" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" -d '{}'
two_factor.phones trả danh sách
{id, label}. Dùng id do API trả về, không
suy ra từ số thứ tự hoặc đuôi điện thoại.
curl --fail-with-body "$BASE/jobs/$JOB_ID/send-code" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"sms","phone_id":1}'
Thay phone_id: 1 bằng ID thực tế. Muốn gửi về thiết bị
tin cậy, dùng {"mode":"trusted_device"}. Chờ
sending = false và code_sent = true, kiểm
tra send_error trước khi nhập mã.
curl --fail-with-body "$BASE/jobs/$JOB_ID/otp" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" -d '{"code":"123456"}'
Mã là chuỗi sáu chữ số, có thể bắt đầu bằng 0. Thay ví dụ bằng mã
của bạn, trước otp_expires_at. Sau khi gửi OTP, tiếp
tục theo dõi đến trạng thái kết thúc. Nếu đang tự nhận OTP từ link,
gọi /automation/cancel với {} trước khi
chuyển sang nhập thủ công.
Lấy mã và liên kết riêng
Khi tiến trình succeeded, đọc lại tài khoản để lấy
device_api. Key cần quyền workshop:code để
nhận liên kết này.
ACCOUNT_ID="ID_TAI_KHOAN"
curl --fail-with-body "$BASE/accounts/$ACCOUNT_ID" \
-H "Authorization: Bearer $XID_API_KEY"
curl --fail-with-body "$BASE/accounts/$ACCOUNT_ID/code" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" -d '{}'
# {"code":"000007","generated_at":"..."}
device_api dùng GET và trả mã dạng văn bản. Ai có liên
kết đều có thể dùng liên kết; giữ riêng như key. Khi server chưa cấu
hình địa chỉ công khai, liên kết có thể là đường dẫn tương đối trên
cùng website.
Thu hồi API Key chặn những yêu cầu API tiếp theo nhưng không đổi
link lấy mã đã tạo. Để vô hiệu link cũ, dùng
POST /accounts/{id}/rotate-link hoặc đổi liên kết trên
web. Dừng tài khoản sẽ chặn việc lấy mã.
Thư mục, ghi chú và gỡ khỏi danh sách
Gửi POST /folders với {"name":"Khách hàng A"}
để tạo thư mục rồi lưu folder.id. Mỗi user có tối đa
200 thư mục; các thư mục chỉ thuộc chủ API key. Gửi ID này trong
folder_id khi thêm hoặc sửa tài khoản.
note nhận tối đa 4.000 ký tự, có thể nhiều dòng.
curl --fail-with-body -X PATCH "$BASE/accounts/$ACCOUNT_ID" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"note":"Khách hàng A\nGia hạn tháng sau","folder_id":"YOUR_FOLDER_ID","hidden":false}'
GET /accounts mặc định dùng visibility=visible.
Chọn hidden để xem tài khoản đã gỡ, hoặc all
để xem cả hai. Thêm folder_id=YOUR_FOLDER_ID để lọc một
thư mục; folder_id=unfiled chỉ lấy tài khoản chưa phân
loại. search cũng tìm trong ghi chú. Danh sách và chi
tiết trả note, folder_id,
folder_name và hidden.
DELETE /accounts/{id} chỉ gỡ khỏi danh sách chính.
Danh tính và device_api được giữ nguyên; phản hồi có
hidden:true và link_preserved:true.
Tài khoản đã gỡ vẫn dùng được API chi tiết/lấy mã và vẫn tính vào
hạn mức. Gửi {"hidden":false} bằng PATCH để đưa lại
danh sách, không cần đăng nhập lại.
Muốn chặn lấy mã, gửi {"enabled":false}; muốn vô hiệu
hóa link cũ, dùng /rotate-link. Link sau khi gỡ vẫn
phụ thuộc dịch vụ/state và việc Apple tiếp tục chấp nhận thiết bị.
Tài khoản đã bị xóa và thu hồi token ở phiên bản cũ không tự được
khôi phục.
Xóa thư mục đưa tài khoản về chưa phân loại, giữ link và ghi chú.
Hoặc gửi {"folder_id":null} khi sửa một tài khoản.
Trên web, xuất TXT có dạng AppleID|link|ghi_chú; cột
ghi chú được bỏ nếu trống. Trong văn bản, \\,
\|, \n, \r lần lượt biểu diễn
dấu gạch chéo ngược, dấu phân cách, xuống dòng và carriage return,
để mỗi tài khoản luôn nằm trên một dòng.
Nhập hàng loạt, xuất TXT và proxy đã lưu
Các endpoint dưới đây dùng chung parser, hàng đợi, hạn mức và cấu hình proxy với Xưởng trên web. Mỗi API Key chỉ thao tác dữ liệu của chính chủ key, kể cả key của super admin.
1. Kiểm tra rồi gửi nhiều tài khoản
Gửi POST /bulk/preview với text trước. Mỗi dòng gồm Apple ID và mật khẩu, phân cách bằng | hoặc khoảng trắng; mật khẩu có khoảng trắng phải đặt trong dấu nháy kép. Có thể thêm link OTP HTTPS hoặc số----link OTP. Đây là định dạng Xưởng; không nhận OTP có sẵn hoặc đáp án bảo mật.
{"text":"one@example.invalid|PASSWORD\ntwo@example.invalid|PASSWORD|14092986901----https://sms.example.invalid/read?key=YOUR_KEY"}
Preview chỉ trả số lượng, tình trạng từng dòng, has_otp_url và masked_phone; không đăng nhập, gửi SMS hay trả lại mật khẩu/link OTP. Sửa dòng không hợp lệ trước khi gửi. Tối đa 100 dòng không rỗng và 256 KiB UTF-8 mỗi lượt.
Gửi cùng text đến POST /bulk với Idempotency-Key mới. Có thể thêm note, folder_id, profile_id, device_model, device_os, device_name và proxy như tạo một tài khoản. HTTP 202 trả batch, gồm id, status, counts và rows. Mỗi user chỉ có một batch hoạt động; API và web chia sẻ giới hạn này.
curl "$BASE/api/v1/workshop/bulk" \
-H "Authorization: Bearer $XID_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $OPERATION_ID" \
--data-binary @accounts.json
GET /bulk trả 20 batch gần nhất. GET /bulk/{batch_id} trả tiến trình từng dòng; dùng job_id với các endpoint /jobs để chọn số, gửi SMS, nhập OTP hoặc dừng tự lấy mã như luồng một tài khoản. Poll khoảng 3 giây/lần, điều phối tổng request để không vượt 120 request/phút. POST /bulk/{batch_id}/cancel dừng dòng chờ và job đang chạy; không xóa tài khoản/link đã hoàn tất.
Không tự gửi lại batch bằng key mới khi mất kết nối. Gửi lại cùng key và JSON sẽ trả batch hiện tại; request_in_progress/request_incomplete có thể kèm batch_id để tra cứu. Mật khẩu/link OTP chỉ ở bộ nhớ khi chạy; restart giữ lịch sử nhưng không tự khôi phục mật khẩu hay chạy lại dòng interrupted. Muốn thử lại, xem kết quả rồi gửi riêng các dòng lỗi với key mới; tài khoản mới/lỗi đủ điều kiện được dùng lại, không tạo bản sao.
2. Xuất các tài khoản đã chọn hoặc toàn bộ
POST /accounts/export cần cả workshop:read và workshop:code vì nội dung chứa link nhận mã. Dùng scope=selected kèm account_ids (tối đa 10.000 ID), hoặc scope=all cùng visibility=visible/hidden/all và folder_id tùy chọn. ID tài khoản/thư mục của người khác làm yêu cầu thất bại, không xuất một phần.
{"scope":"selected","account_ids":["ACCOUNT_ID_1","ACCOUNT_ID_2"]}
Phản hồi JSON gồm text, filename, count, with_link, without_link. Lưu text thành TXT UTF-8: mỗi dòng apple_id|device_api, thêm |note khi có ghi chú. Ký tự gạch chéo ngược, | và xuống dòng trong trường dữ liệu được escape. Tài khoản chưa có link để trống cột link. Xuất chỉ đọc liên kết hiện có, không lấy mã, đăng nhập hay thay danh tính/link.
3. Lưu proxy dùng chung với web
GET /proxy (workshop:read) chỉ trả tóm tắt, không có URL hoặc mật khẩu proxy. PUT /proxy (workshop:write) lưu mode, strategy và value cho các lượt mới; job đang chạy giữ tuyến đã chọn. GET /proxy/details cần workshop:write và trả value đầy đủ của chính user: không ghi log hoặc chia sẻ phản hồi này.
{"mode":"api","strategy":"per_account","value":"https://proxy-provider.example.invalid/next?key=YOUR_KEY"}
Các mode là none/direct/api/list, strategy là per_account/smart. Khi giữ nguyên mode, bỏ value hoặc gửi null/chuỗi trắng giữ giá trị đã lưu; mode=none xóa cấu hình. Tạo tài khoản hoặc batch mà bỏ proxy sẽ dùng cấu hình này. Lưu cấu hình không gọi Apple hoặc nhà cung cấp proxy.
Python
from getpass import getpass
from pathlib import Path
from uuid import uuid4
from workshop_api import Client
client = Client("https://workshop.example.com", getpass("API Key: "))
text = Path("accounts.txt").read_text(encoding="utf-8")
try:
preview = client.preview_bulk(text)
if preview["can_submit"]:
operation_id = str(uuid4())
print("operation_id:", operation_id)
result = client.submit_bulk(text, operation_id=operation_id)
print("batch_id:", result["batch"]["id"])
finally:
text = ""
client.close()
Các endpoint bổ sung
POST /bulk/preview — workshop:writePOST /bulk — workshop:writeGET /bulk — workshop:readGET /bulk/{batch_id} — workshop:readPOST /bulk/{batch_id}/cancel — workshop:writePOST /accounts/export — workshop:read + workshop:codeGET /proxy — workshop:readPUT /proxy — workshop:writeGET /proxy/details — workshop:write
Danh sách API
Các đường dẫn dưới đây nối sau Base URL. Toàn bộ API yêu cầu Bearer
key. Các thao tác POST không có dữ liệu vẫn gửi {};
DELETE cũng cần Content-Type JSON.
| Phương thức | Đường dẫn | Chức năng | Quyền |
|---|---|---|---|
| GET | /me |
Hạn mức và key đang dùng | read |
| GET | /accounts |
Danh sách; page, page_size (tối đa 100), search, status, visibility, folder_id | read |
| GET | /folders |
Thư mục riêng và số tài khoản trong từng thư mục | read |
| POST | /folders |
Tạo thư mục: {"name":"Khách hàng A"} | write |
| PATCH | /folders/{id} |
Đổi tên thư mục: {"name":"Tên mới"} | write |
| DELETE | /folders/{id} |
Xóa thư mục, giữ tài khoản và link | write |
| POST | /accounts |
Thêm và đăng nhập; cần Idempotency-Key | write |
| GET | /accounts/{id} |
Thông tin tài khoản; xem link cần code | read |
| PATCH | /accounts/{id} |
Sửa label, note, folder_id, hidden hoặc enabled | write |
| DELETE | /accounts/{id} |
Gỡ khỏi danh sách, giữ danh tính và link nhận mã | write |
| POST | /accounts/{id}/login |
Đăng nhập lại; password, proxy, otp_source; cần Idempotency-Key | write |
| POST | /accounts/{id}/rotate-link |
Đổi link, link cũ mất hiệu lực | write + code |
| POST | /accounts/{id}/code |
Lấy mã sáu chữ số | code |
| GET | /jobs/{id} |
Đọc tiến trình và lựa chọn 2FA | read |
| POST | /jobs/{id}/factor-options |
Tải lựa chọn nhận mã | write |
| POST | /jobs/{id}/send-code |
Gửi mã tới số/thiết bị đã chọn | write |
| POST | /jobs/{id}/otp |
Nộp mã xác thực | write |
| POST | /jobs/{id}/cancel |
Hủy tiến trình | write |
| POST | /jobs/{id}/automation/cancel |
Dừng đọc link OTP, chuyển sang thủ công | write |
Lỗi, giới hạn và gửi lại yêu cầu
Giới hạn
120 yêu cầu/phút chung cho mỗi user và theo từng key. Khi nhận 429, chờ số giây trong
Retry-After. Hạn mức tài khoản và hàng đợi dùng chung
với web.
Khi lấy mã, 429 code_queue_full là hàng đợi đầy;
503 code_queue_timeout là hết thời gian chờ trước khi
chạy. Cả hai trả Retry-After theo giây. Chờ đủ rồi gọi
lại /code; không cần tạo tài khoản hay đăng nhập lại.
Tạo tài khoản, đăng nhập lại và gửi batch bắt buộc
Idempotency-Key: 8–128 ký tự chữ, số hoặc
._:-, nên dùng UUID. Nếu mất kết nối, gửi lại cùng key,
cùng JSON; hệ thống không tạo thêm tiến trình. Lịch sử key được giữ
7 ngày. Tạo thao tác mới phải dùng key mới.
-
401 api_unauthorized: kiểm tra key, hạn sử dụng và trạng thái user. 403 api_scope_required: key thiếu quyền.-
400 idempotency_key_required: thiếu hoặc sai khóa chống trùng. -
409 idempotency_conflict: key đã dùng với JSON khác. -
409 request_in_progresshoặc503 request_incomplete: chưa xác nhận xong; kiểm tra account/job trước khi gửi thao tác mới. -
409 account_quota_exceeded: đã đạt hạn mức;422 invalid_input: sửa các trường được chỉ ra.
Lỗi trả JSON {"error":"ma_loi","message":"Thông báo"};
có thể kèm account_id, job_id để kiểm tra
kết quả dở dang. Nếu tài khoản đã tạo nhưng chưa xếp được lượt đăng
nhập, giữ ID đó và gọi /accounts/{id}/login sau khi
giải quyết lỗi. Không tự động lặp lại bằng key mới vì có thể tạo
trùng tài khoản.
Các lỗi chi tiết nội bộ không được trả qua API. Tiến trình lỗi có thông báo chung tương tự giao diện web.
Chạy ví dụ Python
File mẫu hỏi key, Apple ID, mật khẩu và OTP tại Terminal, hỗ trợ chọn SMS và proxy. Mật khẩu, key và OTP không hiện khi gõ. Chương trình in ID tiến trình để bạn có thể tiếp tục kiểm tra nếu bị gián đoạn.
Không thêm --proxy thì mẫu kế thừa cấu hình đã lưu.
--proxy nhập một proxy ở chế độ direct,
chiến lược per_account, và cập nhật cấu hình đã lưu.
Để dùng api, list, smart hoặc
none, đặt trường proxy trong body theo
các ví dụ ở trên và hướng dẫn Python chi tiết.
python3 -m pip install requests
python3 workshop_api.py --base-url https://workshop.example.com
# Hỏi thêm proxy cho lần đăng nhập:
python3 workshop_api.py --base-url https://workshop.example.com --proxy
# Tiếp tục theo dõi tiến trình đã tạo:
python3 workshop_api.py --resume-job ID_TIEN_TRINH