Native Shell
Native Shell은 모든 플랫폼의 앱 안에서 만들어지는 AI UI입니다
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기The Python SDK for the Anis Partner API — signed requests, verified responses, typed operations. PyPI: anis-partners
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗The Python client for the Anis Partner API. It signs every request, verifies every answer Anis signs, and gives each operation a typed result. The details that can cause duplicate purchases or expose unverified credentials are handled explicitly: caller-owned order ids, recovery outcomes, and response verification have no silent shortcuts.
Disclaimer. This SDK is an optional helper provided free of charge under the MIT License, "as is", without warranty of any kind. Anis (Aniscom for Technical Services) accepts no responsibility or liability for its use or for any loss arising from it. You remain responsible for your own integration — recording orders before you send them, recovery, key custody and testing. The source code is public: read it to understand exactly what it does before you rely on it. You do not need an SDK — you can integrate directly with the Anis Partner API using the documentation at https://developers.anis.ly.
pip install anis-partners==2.2.0
# or
uv add anis-partners==2.2.0
from anis_partners import AnisPartnersClient, ClientOptions, PemP256Signer
options = ClientOptions("https://<authority Anis gave you>")
signer = PemP256Signer.from_pem_file("/secure/partner-key.pem").for_key("<active key id>")
with AnisPartnersClient(options, signer) as anis:
profile = anis.profile.get()
wallets = list(anis.wallets.list()) # follows every cursor page
print(profile.application.id if profile.application else "No application")
print(f"Found {len(wallets)} wallets")
if wallets:
print(wallets[0].balance.currency)
The async client returns the same results. Run the whole flow inside an event loop:
import asyncio
from anis_partners import AsyncAnisPartnersClient, ClientOptions, PemP256Signer
async def main() -> None:
options = ClientOptions("https://<authority Anis gave you>")
signer = PemP256Signer.from_pem_file("/secure/partner-key.pem").for_key("<active key id>")
async with AsyncAnisPartnersClient(options, signer) as anis:
profile = await anis.profile.get()
wallets = [wallet async for wallet in anis.wallets.list()]
print(profile.application.id if profile.application else "No application")
print(f"Found {len(wallets)} wallets")
if __name__ == "__main__":
asyncio.run(main())
For order creation and recovery, persist the operation id and exact request before calling the SDK. See Orders and recovery for the complete outcome rules.
anis.meter_charges lists packages (get_meter_packages) and charges (get_meter_charges), creates a charge under your own operation id, and reads the receipt, tokens, taxes and refund state. A lost answer is recovered with resume using the same id and body. idempotency_conflict on a create or resume is an unknown outcome (the charge may already be placed), never "not placed": read the operation with get first. get of an operation closed with a recorded refusal raises the typed error with is_replayed set. A failed charge whose refund is still pending is not final.anis.direct_recharge_products lists products, shows their recipient fields and locations, and quotes a payout in LYD or in the foreign amount. anis.direct_recharges creates, resumes and reads a direct recharge, and update_recipient corrects the recipient while the direct recharge is paused (suspendedDataError). It returns the same DirectRechargeProcessing result as a 202 create, with the DirectRechargeAccepted body in recharge and the signed location header on the result, without a direct recharge body. Read the operation later or wait for its webhook.anis.webhook_endpoint sets, reads and tests your endpoint; anis.webhook_events lists deliveries and asks for one again. PartnerWebhookVerifier checks the signature, your URL and your application over the raw bytes before anything is read. See Webhooks.Creating a meter charge or a direct recharge uses the same rules as an order: persist the operation id and exact request first, and resume with the same id. Changing the endpoint, correcting a recipient, sending a test and asking for redelivery are signed changes that are never retried automatically.
The caller supplies the order id. A dropped answer followed by a new id could buy twice, so the SDK never invents this identity. The five order result types distinguish a completed sale, processing, a replay, a definitive refusal, and an unknown outcome. Only the first completion returns credentials; a replay never repeats them.
An unknown result means the original sale may have completed. Resume with the same id and body. A new purchase uses a new id only after a definitive OrderNotPlaced result and a changed intent.
Anis signs the answers that move money, deliver card codes, or establish a key: order, meter charge and direct recharge creation and reads, recipient corrections, webhook endpoint changes, both reveals, application suspension, key revocation, the four enrollment routes, and the signature self-test. On those routes every answer, success or refusal, is verified, and verification cannot be disabled. If such an answer cannot be verified — including one that arrives with no signature — its body is discarded and UnverifiableResponseError is raised. An unverifiable order answer is OrderOutcomeUnknown, because the sale may already have completed.
The information reads — profile, wallets, the catalogue, and the owned-card list and read — are answered unsigned and read without verification. Which routes are signed is fixed per route in the SDK (PartnerRoute.signs_response in PARTNER_ROUTES); it is never decided by whether an answer happens to carry a signature. See Routes and permissions.
The SDK covers every partner route except /v1/application/networks* (Staging only — testing endpoints, not available in production / بيئة الاختبار فقط — نقاط نهاية للاختبار، غير متاحة في الإنتاج), across access, profile, wallets, catalogue, orders, owned_cards, diagnostics, meter_charges, direct_recharge_products, direct_recharges, webhook_endpoint and webhook_events, plus the enrollment client. Public errors are generated from the error catalogue. Request, response, webhook, safety-code, and enrollment vectors are tested along with signed in-memory client flows and contract drift checks.
This package follows the published Partner SDK contract. Supported Python versions are 3.11–3.14.
Signing-key documents are cached for five minutes by default and at most five minutes, including their original age in a shared cache. Unknown response key ids trigger at most one refresh per 30 seconds per key source, including failures. Anis publishes retired response keys for 90 days after retirement and compromised key ids in revoked[] for 90 days; see Security and key custody.
Version 1.x was verified end to end against a live Anis environment in October 2026. Version 2.x has not had a live run yet.
src/anis_partners/errors/_codes.py is generated from contracts/error-catalogue.json. Regenerate it with:
uv run python tools/generate_errors.py
Do not edit the generated file by hand; the contract drift test checks it.
MIT © 2026 Aniscom for Technical Services (Anis).
Use anis.catalogue.get_card(wallet_id, card_id) for one wallet-priced catalogue card. Owned cards use anis.owned_cards and /owned-cards paths. A MaskedCard exposes sold_card_id, operation_id, and a shared CardSummary at card; face value, logo, subcategory and category belong to that summary. Reveals and order sold_cards carry the full RevealedCredential with the same summary. Wallet currency is wallet.balance.currency.
Self-service stops return signed state: anis.access.suspend_application(reason) and anis.access.revoke_key(key_id, reason). Reasons are required and trimmed to 1–500 characters. Resume is staff-only; a revoked key never works again. Both stop calls use a fresh nonce and a body digest without an idempotency key.
1.x is deprecated — upgrade to 2.2.0.