cdo-oracle-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: cdo-oracle-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the RunOnProof decision API
5
+ Author-email: RunOnProof <institucional@aureumone.io>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://runonproof.com
8
+ Project-URL: Documentation, https://docs.runonproof.com/docs
9
+ Project-URL: Repository, https://github.com/runonproof/cdo
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+ Provides-Extra: crypto
13
+ Requires-Dist: cryptography>=45; extra == "crypto"
14
+
15
+ # RunOnProof Python SDK
16
+
17
+ `cdo-oracle-sdk` is the supported Python client for the RunOnProof decision API.
18
+ It exposes synchronous and asynchronous clients for the Brazil and United Kingdom
19
+ decision products, including company checks, supplier approval, invoice and payee
20
+ verification, payment authorization, vendor changes, and Company Passport operations.
21
+
22
+ The client is fail-closed: it preserves evidence, correlation and idempotency fields,
23
+ uses bounded network operations, and does not sign transactions or settle payments.
24
+ See the [RunOnProof documentation](https://docs.runonproof.com/docs) for API setup and
25
+ credential handling.
@@ -0,0 +1,11 @@
1
+ # RunOnProof Python SDK
2
+
3
+ `cdo-oracle-sdk` is the supported Python client for the RunOnProof decision API.
4
+ It exposes synchronous and asynchronous clients for the Brazil and United Kingdom
5
+ decision products, including company checks, supplier approval, invoice and payee
6
+ verification, payment authorization, vendor changes, and Company Passport operations.
7
+
8
+ The client is fail-closed: it preserves evidence, correlation and idempotency fields,
9
+ uses bounded network operations, and does not sign transactions or settle payments.
10
+ See the [RunOnProof documentation](https://docs.runonproof.com/docs) for API setup and
11
+ credential handling.
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cdo-oracle-sdk"
7
+ version = "0.1.0"
8
+ requires-python = ">=3.11"
9
+ description = "Python SDK for the RunOnProof decision API"
10
+ readme = "README.md"
11
+ license = "MIT"
12
+ authors = [{ name = "RunOnProof", email = "institucional@aureumone.io" }]
13
+ dependencies = []
14
+
15
+ [project.urls]
16
+ Homepage = "https://runonproof.com"
17
+ Documentation = "https://docs.runonproof.com/docs"
18
+ Repository = "https://github.com/runonproof/cdo"
19
+
20
+ [project.optional-dependencies]
21
+ crypto = ["cryptography>=45"]
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .client import OracleClient, OracleError, verify_ed25519_token
2
+
3
+ __all__ = ["OracleClient", "OracleError", "verify_ed25519_token"]
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+ import asyncio, base64, json, time
3
+ from dataclasses import dataclass
4
+ from typing import Any, Mapping
5
+ from urllib.error import HTTPError
6
+ from urllib.parse import quote
7
+ from urllib.request import Request, urlopen
8
+
9
+ class OracleError(RuntimeError):
10
+ def __init__(self, code: str, status: int = 0, details: Any = None): super().__init__(code); self.code=code; self.status=status; self.details=details
11
+
12
+ @dataclass(frozen=True)
13
+ class ClientOptions:
14
+ base_url: str
15
+ bearer_token: str | None = None
16
+ timeout: float = 8.0
17
+ retries: int = 2
18
+
19
+ class Transport:
20
+ def __init__(self, options: ClientOptions): self.options=options
21
+ def request(self, method: str, path: str, body: Mapping[str,Any] | None=None, *, idempotency_key: str | None=None, payment_signature: str | None=None, request_body_digest: str | None=None, timeout: float | None=None) -> dict[str,Any]:
22
+ payload=dict(body or {}); headers={"Accept":"application/json"}
23
+ if idempotency_key: payload["idempotency_key"]=idempotency_key; headers["Idempotency-Key"]=idempotency_key
24
+ if payment_signature: headers["Payment-Signature"]=payment_signature
25
+ if request_body_digest: headers["X-CDO-Request-Body-Digest"]=request_body_digest
26
+ if self.options.bearer_token: headers["Authorization"]=f"Bearer {self.options.bearer_token}"
27
+ data=None if body is None else json.dumps(payload,separators=(",", ":")).encode();
28
+ if data is not None: headers["Content-Type"]="application/json"
29
+ for attempt in range(self.options.retries+1):
30
+ try:
31
+ with urlopen(Request(self.options.base_url.rstrip("/")+path,data=data,headers=headers,method=method),timeout=timeout or self.options.timeout) as response: return json.loads(response.read())
32
+ except HTTPError as error:
33
+ details=_json(error.read()); retryable=error.code in (429,500,502,503,504) and (method=="GET" or idempotency_key is not None)
34
+ if retryable and attempt<self.options.retries: time.sleep(.15*(2**attempt)); continue
35
+ raise OracleError(str(details.get("code",f"HTTP_{error.code}")),error.code,details) from error
36
+ async def arequest(self,*args:Any,**kwargs:Any)->dict[str,Any]: return await asyncio.to_thread(self.request,*args,**kwargs)
37
+
38
+ class UkClient:
39
+ def __init__(self, transport:Transport): self._t=transport
40
+ def _post(self,path:str,payload:Mapping[str,Any],idempotency_key:str|None=None,payment_signature:str|None=None,request_body_digest:str|None=None)->dict[str,Any]: return _validate_envelope(self._t.request("POST",path,payload,idempotency_key=idempotency_key,payment_signature=payment_signature,request_body_digest=request_body_digest))
41
+ def resolve_legal_entity(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/entities/resolve",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
42
+ def verify_company_status(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/companies/legal-status",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
43
+ def match_invoice_to_company(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/invoices/match-company",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
44
+ def match_business_payee(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/payees/match-business",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
45
+ def authorize_vendor_payment_detail_change(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/vendors/authorize-payment-detail-change",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
46
+ def screen_restricted_party(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/restricted-parties/screen",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
47
+ def continue_vendor_authorization(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/vendors/authorizations/continue",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
48
+ def assess_invoice_authenticity_duplication(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/invoices/assess-authenticity-duplication",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
49
+ def verify_bank_account_ownership(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/bank-accounts/verify-ownership",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
50
+ def detect_company_material_change(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/companies/detect-material-change",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
51
+ def verify_business_licence(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/business-licences/verify",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
52
+ def verify_tax_registration(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/tax-registrations/verify",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
53
+ def verify_vat_registration(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self.verify_tax_registration(payload,**kw)
54
+ def validate_business_document(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/business-documents/validate",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
55
+ def verify_beneficial_ownership_control(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/beneficial-ownership-control/verify",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
56
+ def assess_source_evidence_freshness(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/evidence/assess-freshness",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
57
+ def verify_invoice_and_payee(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/solutions/invoice-payee-verification",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
58
+ def approve_supplier(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/suppliers/approve",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
59
+ def authorize_business_payment(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/payments/authorize",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
60
+ def create_company_capability_passport(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return _validate_passport(self._t.request("POST","/v1/gb/company-passports",payload,idempotency_key=kw.get("idempotency_key")))
61
+ def get_company_capability_passport(self,passport_id:str)->dict[str,Any]: return _validate_passport(self._t.request("GET",f"/v1/gb/company-passports/{quote(passport_id,safe='')}"))
62
+ def get_company_capability_passport_version(self,passport_id:str,version:int)->dict[str,Any]: return _validate_passport(self._t.request("GET",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/versions/{version}"))
63
+ def replay_company_capability_passport(self,passport_id:str,version:int)->dict[str,Any]: return self._t.request("GET",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/replays/{version}")
64
+ def verify_company_capability_passport(self,passport_id:str)->dict[str,Any]: return self._t.request("GET",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/verify")
65
+ def verify_company_capability_passport_version(self,passport_id:str,version:int)->dict[str,Any]: return self._t.request("GET",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/versions/{version}/verify")
66
+ def update_company_capability_passport(self,passport_id:str,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return _validate_passport(self._t.request("POST",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/updates",payload,idempotency_key=kw.get("idempotency_key")))
67
+ def refresh_company_capability_passport(self,passport_id:str,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return _validate_passport(self._t.request("POST",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/refresh",payload,idempotency_key=kw.get("idempotency_key")))
68
+ def invalidate_company_capability_passport(self,passport_id:str,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return _validate_passport(self._t.request("POST",f"/v1/gb/company-passports/{quote(passport_id,safe='')}/invalidate",payload,idempotency_key=kw.get("idempotency_key")))
69
+ def company_check(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/solutions/company-check",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
70
+ def supplier_approval(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/solutions/supplier-approval",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
71
+ def invoice_payee_verification(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/solutions/invoice-payee-verification",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
72
+ def payment_authorization(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/solutions/payment-authorization",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
73
+ def vendor_change_continuous_authorization(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return self._post("/v1/gb/solutions/vendor-change-continuous-authorization",payload,kw.get("idempotency_key"),kw.get("payment_signature"),kw.get("request_body_digest"))
74
+ def replay(self,execution_id:str)->dict[str,Any]: return _validate_envelope(self._t.request("GET",f"/v1/gb/executions/{quote(execution_id,safe='')}"))
75
+ def consume_token(self,token:str,usage_id:str)->dict[str,Any]:
76
+ value=self._t.request("POST","/v1/gb/tokens/consume",{"token":token,"usage_id":usage_id})
77
+ if not isinstance(value.get("state"),str): raise OracleError("RESPONSE_SCHEMA_INVALID",502,value)
78
+ return value
79
+ async def aresolve_legal_entity(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.resolve_legal_entity,payload,**kw)
80
+ async def averify_company_status(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_company_status,payload,**kw)
81
+ async def ascreen_restricted_party(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.screen_restricted_party,payload,**kw)
82
+ async def acontinue_vendor_authorization(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.continue_vendor_authorization,payload,**kw)
83
+ async def aassess_invoice_authenticity_duplication(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.assess_invoice_authenticity_duplication,payload,**kw)
84
+ async def averify_bank_account_ownership(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_bank_account_ownership,payload,**kw)
85
+ async def adetect_company_material_change(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.detect_company_material_change,payload,**kw)
86
+ async def averify_business_licence(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_business_licence,payload,**kw)
87
+ async def averify_vat_registration(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_vat_registration,payload,**kw)
88
+ async def averify_tax_registration(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_tax_registration,payload,**kw)
89
+ async def avalidate_business_document(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.validate_business_document,payload,**kw)
90
+ async def averify_beneficial_ownership_control(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_beneficial_ownership_control,payload,**kw)
91
+ async def aassess_source_evidence_freshness(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.assess_source_evidence_freshness,payload,**kw)
92
+ async def averify_invoice_and_payee(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.verify_invoice_and_payee,payload,**kw)
93
+ async def aapprove_supplier(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.approve_supplier,payload,**kw)
94
+ async def aauthorize_business_payment(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.authorize_business_payment,payload,**kw)
95
+ async def acreate_company_capability_passport(self,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.create_company_capability_passport,payload,**kw)
96
+ async def aget_company_capability_passport(self,passport_id:str)->dict[str,Any]: return await asyncio.to_thread(self.get_company_capability_passport,passport_id)
97
+ async def aget_company_capability_passport_version(self,passport_id:str,version:int)->dict[str,Any]: return await asyncio.to_thread(self.get_company_capability_passport_version,passport_id,version)
98
+ async def areplay_company_capability_passport(self,passport_id:str,version:int)->dict[str,Any]: return await asyncio.to_thread(self.replay_company_capability_passport,passport_id,version)
99
+ async def averify_company_capability_passport(self,passport_id:str)->dict[str,Any]: return await asyncio.to_thread(self.verify_company_capability_passport,passport_id)
100
+ async def averify_company_capability_passport_version(self,passport_id:str,version:int)->dict[str,Any]: return await asyncio.to_thread(self.verify_company_capability_passport_version,passport_id,version)
101
+ async def aupdate_company_capability_passport(self,passport_id:str,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.update_company_capability_passport,passport_id,payload,**kw)
102
+ async def arefresh_company_capability_passport(self,passport_id:str,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.refresh_company_capability_passport,passport_id,payload,**kw)
103
+ async def ainvalidate_company_capability_passport(self,passport_id:str,payload:Mapping[str,Any],**kw:Any)->dict[str,Any]: return await asyncio.to_thread(self.invalidate_company_capability_passport,passport_id,payload,**kw)
104
+ async def areplay(self,execution_id:str)->dict[str,Any]: return await asyncio.to_thread(self.replay,execution_id)
105
+ async def aconsume_token(self,token:str,usage_id:str)->dict[str,Any]: return await asyncio.to_thread(self.consume_token,token,usage_id)
106
+
107
+ class OracleClient:
108
+ def __init__(self,base_url:str,*,bearer_token:str|None=None,timeout:float=8,retries:int=2): self.transport=Transport(ClientOptions(base_url,bearer_token,timeout,retries)); self.uk=UkClient(self.transport)
109
+
110
+ def verify_ed25519_token(token:str,public_key_pem:bytes,*,audience:str|None=None,now:int|None=None)->dict[str,Any]:
111
+ try:
112
+ from cryptography.hazmat.primitives import serialization
113
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
114
+ except ImportError as error: raise OracleError("CRYPTOGRAPHY_EXTRA_REQUIRED") from error
115
+ parts=token.split(".");
116
+ if len(parts)!=3 or len(token.encode())>8192: raise OracleError("TOKEN_MALFORMED")
117
+ header=_decode(parts[0]); claims=_decode(parts[1]);
118
+ if header.get("alg")!="EdDSA": raise OracleError("TOKEN_ALGORITHM_UNSUPPORTED")
119
+ key=serialization.load_pem_public_key(public_key_pem)
120
+ if not isinstance(key,Ed25519PublicKey): raise OracleError("TOKEN_KEY_INVALID")
121
+ try: key.verify(_b64(parts[2]),f"{parts[0]}.{parts[1]}".encode())
122
+ except Exception as error: raise OracleError("TOKEN_SIGNATURE_INVALID") from error
123
+ current=now or int(time.time())
124
+ if isinstance(claims.get("not_before"),int) and current<claims["not_before"]: raise OracleError("TOKEN_NOT_YET_VALID")
125
+ if isinstance(claims.get("expires_at"),int) and current>=claims["expires_at"]: raise OracleError("TOKEN_EXPIRED")
126
+ if audience is not None and claims.get("audience")!=audience: raise OracleError("TOKEN_AUDIENCE_MISMATCH")
127
+ return claims
128
+ def _b64(value:str)->bytes: return base64.urlsafe_b64decode(value+"="*((4-len(value)%4)%4))
129
+ def _decode(value:str)->dict[str,Any]:
130
+ try: return json.loads(_b64(value))
131
+ except Exception as error: raise OracleError("TOKEN_MALFORMED") from error
132
+ def _json(raw:bytes)->dict[str,Any]:
133
+ try: value=json.loads(raw); return value if isinstance(value,dict) else {"value":value}
134
+ except Exception: return {}
135
+ def _validate_envelope(value:dict[str,Any])->dict[str,Any]:
136
+ economic=value.get("economic_activity")
137
+ if not isinstance(value.get("schema_version"),str) or not isinstance(value.get("product_or_solution"),str) or value.get("decision") not in ("APPROVE","CONDITIONAL","REVIEW","BLOCK","RETRY","NO_DECISION") or not isinstance(economic,dict) or not isinstance(economic.get("charged"),bool) or not isinstance(economic.get("quote_created"),bool) or not isinstance(economic.get("settlement_created"),bool) or "result" not in value: raise OracleError("RESPONSE_SCHEMA_INVALID",502,value)
138
+ return value
139
+ def _validate_passport(value:dict[str,Any])->dict[str,Any]:
140
+ if not isinstance(value.get("passport_id"),str) or not isinstance(value.get("passport_version"),int) or not isinstance(value.get("passport_digest"),str) or value.get("economic_mutations") != 0: raise OracleError("RESPONSE_SCHEMA_INVALID",502,value)
141
+ return value
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: cdo-oracle-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the RunOnProof decision API
5
+ Author-email: RunOnProof <institucional@aureumone.io>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://runonproof.com
8
+ Project-URL: Documentation, https://docs.runonproof.com/docs
9
+ Project-URL: Repository, https://github.com/runonproof/cdo
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+ Provides-Extra: crypto
13
+ Requires-Dist: cryptography>=45; extra == "crypto"
14
+
15
+ # RunOnProof Python SDK
16
+
17
+ `cdo-oracle-sdk` is the supported Python client for the RunOnProof decision API.
18
+ It exposes synchronous and asynchronous clients for the Brazil and United Kingdom
19
+ decision products, including company checks, supplier approval, invoice and payee
20
+ verification, payment authorization, vendor changes, and Company Passport operations.
21
+
22
+ The client is fail-closed: it preserves evidence, correlation and idempotency fields,
23
+ uses bounded network operations, and does not sign transactions or settle payments.
24
+ See the [RunOnProof documentation](https://docs.runonproof.com/docs) for API setup and
25
+ credential handling.
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/cdo_oracle/__init__.py
4
+ src/cdo_oracle/client.py
5
+ src/cdo_oracle_sdk.egg-info/PKG-INFO
6
+ src/cdo_oracle_sdk.egg-info/SOURCES.txt
7
+ src/cdo_oracle_sdk.egg-info/dependency_links.txt
8
+ src/cdo_oracle_sdk.egg-info/requires.txt
9
+ src/cdo_oracle_sdk.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+
2
+ [crypto]
3
+ cryptography>=45