identifyorg 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,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: identifyorg
3
+ Version: 0.1.0
4
+ Summary: IdentifyOrg server-side SDK — identity verification, prepaid billing, and realtime call/chat token issuance.
5
+ License: MIT
6
+ Keywords: identifyorg,kyc,bvn,nin,identity-verification,livekit
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: requests>=2.28
10
+
11
+ # identifyorg (Python server SDK)
12
+
13
+ ```bash
14
+ pip install identifyorg
15
+ ```
16
+
17
+ ```python
18
+ import os
19
+ from identifyorg import IdentifyOrgClient, IdentifyOrgApiError
20
+
21
+ identifyorg = IdentifyOrgClient(api_key=os.environ["IDENTIFYORG_SECRET_KEY"])
22
+
23
+ # Identity verification
24
+ result = identifyorg.verify_bvn("22212345678", first_name="Ada", last_name="Okafor",
25
+ idempotency_key="order-123")
26
+
27
+ # Top up credit
28
+ topup = identifyorg.topup(amount=5000)
29
+ print(topup["checkout_url"])
30
+
31
+ # Mint a realtime token for YOUR user, then hand it to the browser/mobile
32
+ # client (IdentifyOrg JS/React Native/Flutter/Kotlin SDK) — never send your
33
+ # secret key to a client.
34
+ call = identifyorg.streaming_token("video", identity=user.id, display_name=user.name)
35
+ return {"token": call["token"], "url": call["url"], "room_name": call["room_name"]}
36
+
37
+ chat = identifyorg.chat_token(visitor_id=visitor.id, visitor_name=visitor.name)
38
+ ```
39
+
40
+ Errors raise `IdentifyOrgApiError` with `.status` and `.code` attributes.
41
+
42
+ ## Django / Flask example: minting a call token endpoint
43
+
44
+ ```python
45
+ @app.post("/api/call-token")
46
+ def call_token():
47
+ return identifyorg.streaming_token("video", identity=current_user.id, display_name=current_user.name)
48
+ ```
49
+
50
+ Your frontend calls this endpoint (never IdentifyOrg directly with a secret
51
+ key), then passes the returned `token`/`url` straight into the IdentifyOrg JS
52
+ SDK's `joinCall()`.
@@ -0,0 +1,42 @@
1
+ # identifyorg (Python server SDK)
2
+
3
+ ```bash
4
+ pip install identifyorg
5
+ ```
6
+
7
+ ```python
8
+ import os
9
+ from identifyorg import IdentifyOrgClient, IdentifyOrgApiError
10
+
11
+ identifyorg = IdentifyOrgClient(api_key=os.environ["IDENTIFYORG_SECRET_KEY"])
12
+
13
+ # Identity verification
14
+ result = identifyorg.verify_bvn("22212345678", first_name="Ada", last_name="Okafor",
15
+ idempotency_key="order-123")
16
+
17
+ # Top up credit
18
+ topup = identifyorg.topup(amount=5000)
19
+ print(topup["checkout_url"])
20
+
21
+ # Mint a realtime token for YOUR user, then hand it to the browser/mobile
22
+ # client (IdentifyOrg JS/React Native/Flutter/Kotlin SDK) — never send your
23
+ # secret key to a client.
24
+ call = identifyorg.streaming_token("video", identity=user.id, display_name=user.name)
25
+ return {"token": call["token"], "url": call["url"], "room_name": call["room_name"]}
26
+
27
+ chat = identifyorg.chat_token(visitor_id=visitor.id, visitor_name=visitor.name)
28
+ ```
29
+
30
+ Errors raise `IdentifyOrgApiError` with `.status` and `.code` attributes.
31
+
32
+ ## Django / Flask example: minting a call token endpoint
33
+
34
+ ```python
35
+ @app.post("/api/call-token")
36
+ def call_token():
37
+ return identifyorg.streaming_token("video", identity=current_user.id, display_name=current_user.name)
38
+ ```
39
+
40
+ Your frontend calls this endpoint (never IdentifyOrg directly with a secret
41
+ key), then passes the returned `token`/`url` straight into the IdentifyOrg JS
42
+ SDK's `joinCall()`.
@@ -0,0 +1,12 @@
1
+ """IdentifyOrg — Python server SDK.
2
+
3
+ Server-side only: use your SECRET key here (vl_test_sk_.../vl_live_sk_...).
4
+ For browser/mobile clients, mint a realtime token with `client.streaming_token(...)`
5
+ or `client.chat_token(...)` on your backend and hand the result to the
6
+ IdentifyOrg JS/React Native/Flutter/Kotlin SDK — never ship this key to a client.
7
+ """
8
+
9
+ from .client import IdentifyOrgClient, IdentifyOrgApiError
10
+
11
+ __all__ = ["IdentifyOrgClient", "IdentifyOrgApiError"]
12
+ __version__ = "0.1.0"
@@ -0,0 +1,222 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+ from urllib.parse import urlencode
5
+
6
+ import requests
7
+
8
+ DEFAULT_BASE_URL = "https://api.identifyorg.com"
9
+
10
+
11
+ class IdentifyOrgApiError(Exception):
12
+ def __init__(self, status: int, code: str | None, message: str):
13
+ self.status = status
14
+ self.code = code
15
+ super().__init__(f"IdentifyOrg API error ({code or status}): {message}")
16
+
17
+
18
+ class IdentifyOrgClient:
19
+ """Server-side client. `api_key` must be a SECRET key
20
+ (vl_test_sk_.../vl_live_sk_...) — never expose it in client-side code.
21
+ """
22
+
23
+ def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
24
+ if not api_key:
25
+ raise ValueError("IdentifyOrg: api_key is required")
26
+ self.api_key = api_key
27
+ self.base_url = base_url.rstrip("/")
28
+ self._session = requests.Session()
29
+
30
+ # --- verification ---
31
+
32
+ def verify_bvn(self, bvn: str, first_name: str | None = None,
33
+ last_name: str | None = None, idempotency_key: str | None = None) -> dict:
34
+ return self._post(
35
+ "/v1/verify/bvn",
36
+ {"bvn": bvn, "first_name": first_name, "last_name": last_name},
37
+ idempotency_key,
38
+ )
39
+
40
+ def verify_nin(self, nin: str, first_name: str | None = None,
41
+ last_name: str | None = None, idempotency_key: str | None = None) -> dict:
42
+ return self._post(
43
+ "/v1/verify/nin",
44
+ {"nin": nin, "first_name": first_name, "last_name": last_name},
45
+ idempotency_key,
46
+ )
47
+
48
+ def verify_frsc(self, licence_number: str, first_name: str | None = None,
49
+ last_name: str | None = None, idempotency_key: str | None = None) -> dict:
50
+ return self._post(
51
+ "/v1/verify/frsc",
52
+ {"licence_number": licence_number, "first_name": first_name, "last_name": last_name},
53
+ idempotency_key,
54
+ )
55
+
56
+ def verify_credit_score(self, bvn: str, first_name: str | None = None,
57
+ last_name: str | None = None, idempotency_key: str | None = None) -> dict:
58
+ return self._post(
59
+ "/v1/verify/credit-score",
60
+ {"bvn": bvn, "first_name": first_name, "last_name": last_name},
61
+ idempotency_key,
62
+ )
63
+
64
+ def verify_cac(self, rc_number: str, first_name: str | None = None,
65
+ last_name: str | None = None, idempotency_key: str | None = None) -> dict:
66
+ return self._post(
67
+ "/v1/verify/cac",
68
+ {"rc_number": rc_number, "first_name": first_name, "last_name": last_name},
69
+ idempotency_key,
70
+ )
71
+
72
+ # --- realtime: calls, streaming, chat ---
73
+
74
+ def streaming_token(self, session_type: str, identity: str,
75
+ display_name: str | None = None, room_name: str | None = None,
76
+ role: str = "publisher") -> dict:
77
+ """session_type: 'video' | 'voice' | 'stream'. Mint this server-side,
78
+ then hand the result to a client SDK (JS/React Native/Flutter/Kotlin)."""
79
+ return self._post(
80
+ f"/v1/streaming/{session_type}/token",
81
+ {
82
+ "identity": identity,
83
+ "display_name": display_name,
84
+ "room_name": room_name,
85
+ "role": role,
86
+ },
87
+ )
88
+
89
+ def streaming_sessions(self, limit: int = 50) -> list[dict]:
90
+ return self._get("/v1/streaming/sessions", {"limit": limit})
91
+
92
+ # --- host moderation — call from your backend in response to your own
93
+ # host UI (mute/kick/end buttons), never directly from a client ---
94
+
95
+ def list_participants(self, session_id: str) -> list[dict]:
96
+ return self._get(f"/v1/streaming/sessions/{session_id}/participants")
97
+
98
+ def mute_participant(self, session_id: str, identity: str) -> dict:
99
+ return self._post(f"/v1/streaming/sessions/{session_id}/participants/{identity}/mute", {})
100
+
101
+ def unmute_participant(self, session_id: str, identity: str) -> dict:
102
+ return self._post(f"/v1/streaming/sessions/{session_id}/participants/{identity}/unmute", {})
103
+
104
+ def camera_off_participant(self, session_id: str, identity: str) -> dict:
105
+ return self._post(
106
+ f"/v1/streaming/sessions/{session_id}/participants/{identity}/camera-off", {}
107
+ )
108
+
109
+ def camera_on_participant(self, session_id: str, identity: str) -> dict:
110
+ return self._post(
111
+ f"/v1/streaming/sessions/{session_id}/participants/{identity}/camera-on", {}
112
+ )
113
+
114
+ def kick_participant(self, session_id: str, identity: str) -> dict:
115
+ return self._post(f"/v1/streaming/sessions/{session_id}/participants/{identity}/kick", {})
116
+
117
+ def end_session(self, session_id: str) -> dict:
118
+ return self._post(f"/v1/streaming/sessions/{session_id}/end", {})
119
+
120
+ # --- the gifting economy ---
121
+
122
+ def create_gift(self, name: str, price_coins: int, emoji: str = "🎁") -> dict:
123
+ return self._post(
124
+ "/v1/streaming/gifts", {"name": name, "emoji": emoji, "price_coins": price_coins}
125
+ )
126
+
127
+ def list_gifts(self, include_inactive: bool = False) -> list[dict]:
128
+ return self._get("/v1/streaming/gifts", {"include_inactive": include_inactive})
129
+
130
+ def update_gift(self, gift_id: str, **fields: Any) -> dict:
131
+ return self._patch(f"/v1/streaming/gifts/{gift_id}", fields)
132
+
133
+ def delete_gift(self, gift_id: str) -> None:
134
+ self._delete(f"/v1/streaming/gifts/{gift_id}")
135
+
136
+ def credit_coins(self, external_id: str, amount: int, display_name: str | None = None) -> dict:
137
+ return self._post(
138
+ f"/v1/streaming/viewers/{external_id}/coins/credit",
139
+ {"amount": amount, "display_name": display_name},
140
+ )
141
+
142
+ def get_viewer(self, external_id: str) -> dict:
143
+ return self._get(f"/v1/streaming/viewers/{external_id}")
144
+
145
+ def send_gift(self, from_external_id: str, to_identity: str, gift_id: str, room_name: str) -> dict:
146
+ return self._post(
147
+ "/v1/streaming/gifts/send",
148
+ {
149
+ "from_external_id": from_external_id,
150
+ "to_identity": to_identity,
151
+ "gift_id": gift_id,
152
+ "room_name": room_name,
153
+ },
154
+ )
155
+
156
+ def chat_token(self, visitor_id: str, visitor_name: str | None = None,
157
+ channel_id: str | None = None) -> dict:
158
+ return self._post(
159
+ "/v1/chat/token",
160
+ {"visitor_id": visitor_id, "visitor_name": visitor_name, "channel_id": channel_id},
161
+ )
162
+
163
+ # --- billing ---
164
+
165
+ def topup(self, amount: float) -> dict:
166
+ return self._post("/v1/payments/topup", {"amount": amount})
167
+
168
+ def payments(self, limit: int = 50) -> list[dict]:
169
+ return self._get("/v1/payments", {"limit": limit})
170
+
171
+ def balance(self) -> dict:
172
+ return self._get("/v1/balance")
173
+
174
+ def transactions(self, limit: int = 50, offset: int = 0) -> dict:
175
+ return self._get("/v1/transactions", {"limit": limit, "offset": offset})
176
+
177
+ def usage(self, **filters: Any) -> dict:
178
+ return self._get("/v1/usage", filters)
179
+
180
+ def pricing(self) -> list[dict]:
181
+ return self._get("/v1/pricing")
182
+
183
+ # --- internals ---
184
+
185
+ def _headers(self, idempotency_key: str | None = None) -> dict:
186
+ headers = {"X-IdentifyOrg-Key": self.api_key}
187
+ if idempotency_key:
188
+ headers["Idempotency-Key"] = idempotency_key
189
+ return headers
190
+
191
+ def _post(self, path: str, body: dict, idempotency_key: str | None = None) -> dict:
192
+ body = {k: v for k, v in body.items() if v is not None}
193
+ resp = self._session.post(
194
+ self.base_url + path, json=body, headers=self._headers(idempotency_key)
195
+ )
196
+ return self._parse(resp)
197
+
198
+ def _patch(self, path: str, body: dict) -> dict:
199
+ body = {k: v for k, v in body.items() if v is not None}
200
+ resp = self._session.patch(self.base_url + path, json=body, headers=self._headers())
201
+ return self._parse(resp)
202
+
203
+ def _delete(self, path: str) -> None:
204
+ resp = self._session.delete(self.base_url + path, headers=self._headers())
205
+ self._parse(resp)
206
+
207
+ def _get(self, path: str, params: dict | None = None) -> Any:
208
+ params = {k: v for k, v in (params or {}).items() if v is not None}
209
+ query = f"?{urlencode(params)}" if params else ""
210
+ resp = self._session.get(self.base_url + path + query, headers=self._headers())
211
+ return self._parse(resp)
212
+
213
+ @staticmethod
214
+ def _parse(resp: requests.Response) -> Any:
215
+ try:
216
+ data = resp.json()
217
+ except ValueError:
218
+ data = {}
219
+ if not resp.ok:
220
+ err = data.get("error", {}) if isinstance(data, dict) else {}
221
+ raise IdentifyOrgApiError(resp.status_code, err.get("code"), err.get("message", resp.reason))
222
+ return data
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: identifyorg
3
+ Version: 0.1.0
4
+ Summary: IdentifyOrg server-side SDK — identity verification, prepaid billing, and realtime call/chat token issuance.
5
+ License: MIT
6
+ Keywords: identifyorg,kyc,bvn,nin,identity-verification,livekit
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: requests>=2.28
10
+
11
+ # identifyorg (Python server SDK)
12
+
13
+ ```bash
14
+ pip install identifyorg
15
+ ```
16
+
17
+ ```python
18
+ import os
19
+ from identifyorg import IdentifyOrgClient, IdentifyOrgApiError
20
+
21
+ identifyorg = IdentifyOrgClient(api_key=os.environ["IDENTIFYORG_SECRET_KEY"])
22
+
23
+ # Identity verification
24
+ result = identifyorg.verify_bvn("22212345678", first_name="Ada", last_name="Okafor",
25
+ idempotency_key="order-123")
26
+
27
+ # Top up credit
28
+ topup = identifyorg.topup(amount=5000)
29
+ print(topup["checkout_url"])
30
+
31
+ # Mint a realtime token for YOUR user, then hand it to the browser/mobile
32
+ # client (IdentifyOrg JS/React Native/Flutter/Kotlin SDK) — never send your
33
+ # secret key to a client.
34
+ call = identifyorg.streaming_token("video", identity=user.id, display_name=user.name)
35
+ return {"token": call["token"], "url": call["url"], "room_name": call["room_name"]}
36
+
37
+ chat = identifyorg.chat_token(visitor_id=visitor.id, visitor_name=visitor.name)
38
+ ```
39
+
40
+ Errors raise `IdentifyOrgApiError` with `.status` and `.code` attributes.
41
+
42
+ ## Django / Flask example: minting a call token endpoint
43
+
44
+ ```python
45
+ @app.post("/api/call-token")
46
+ def call_token():
47
+ return identifyorg.streaming_token("video", identity=current_user.id, display_name=current_user.name)
48
+ ```
49
+
50
+ Your frontend calls this endpoint (never IdentifyOrg directly with a secret
51
+ key), then passes the returned `token`/`url` straight into the IdentifyOrg JS
52
+ SDK's `joinCall()`.
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ identifyorg/__init__.py
4
+ identifyorg/client.py
5
+ identifyorg.egg-info/PKG-INFO
6
+ identifyorg.egg-info/SOURCES.txt
7
+ identifyorg.egg-info/dependency_links.txt
8
+ identifyorg.egg-info/requires.txt
9
+ identifyorg.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.28
@@ -0,0 +1 @@
1
+ identifyorg
@@ -0,0 +1,16 @@
1
+ [project]
2
+ name = "identifyorg"
3
+ version = "0.1.0"
4
+ description = "IdentifyOrg server-side SDK — identity verification, prepaid billing, and realtime call/chat token issuance."
5
+ readme = "README.md"
6
+ requires-python = ">=3.9"
7
+ license = { text = "MIT" }
8
+ dependencies = ["requests>=2.28"]
9
+ keywords = ["identifyorg", "kyc", "bvn", "nin", "identity-verification", "livekit"]
10
+
11
+ [build-system]
12
+ requires = ["setuptools>=61.0"]
13
+ build-backend = "setuptools.build_meta"
14
+
15
+ [tool.setuptools.packages.find]
16
+ include = ["identifyorg*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+