bitgen-sdk 1.0.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.
- bitgen_sdk-1.0.0/.gitignore +8 -0
- bitgen_sdk-1.0.0/CHANGELOG.md +23 -0
- bitgen_sdk-1.0.0/PKG-INFO +66 -0
- bitgen_sdk-1.0.0/README.md +37 -0
- bitgen_sdk-1.0.0/pyproject.toml +74 -0
- bitgen_sdk-1.0.0/readme/concepts.md +128 -0
- bitgen_sdk-1.0.0/readme/configuration.md +74 -0
- bitgen_sdk-1.0.0/readme/errors.md +73 -0
- bitgen_sdk-1.0.0/readme/installation.md +39 -0
- bitgen_sdk-1.0.0/readme/media/activation.svg +58 -0
- bitgen_sdk-1.0.0/readme/media/deposit-flow.svg +70 -0
- bitgen_sdk-1.0.0/readme/media/order-buy.svg +43 -0
- bitgen_sdk-1.0.0/readme/media/order-sell.svg +47 -0
- bitgen_sdk-1.0.0/readme/media/purchase-flow.svg +61 -0
- bitgen_sdk-1.0.0/readme/media/sale-flow.svg +61 -0
- bitgen_sdk-1.0.0/readme/media/staking-position.svg +75 -0
- bitgen_sdk-1.0.0/readme/media/transaction-lifecycle.svg +61 -0
- bitgen_sdk-1.0.0/readme/media/webhook-delivery.svg +53 -0
- bitgen_sdk-1.0.0/readme/media/withdrawal-flow.svg +64 -0
- bitgen_sdk-1.0.0/readme/quick-start.md +90 -0
- bitgen_sdk-1.0.0/readme/resource/apikeys.md +99 -0
- bitgen_sdk-1.0.0/readme/resource/asset.md +119 -0
- bitgen_sdk-1.0.0/readme/resource/bank.md +155 -0
- bitgen_sdk-1.0.0/readme/resource/core.md +90 -0
- bitgen_sdk-1.0.0/readme/resource/custody.md +170 -0
- bitgen_sdk-1.0.0/readme/resource/customer.md +206 -0
- bitgen_sdk-1.0.0/readme/resource/staking.md +274 -0
- bitgen_sdk-1.0.0/readme/resource/trading.md +160 -0
- bitgen_sdk-1.0.0/readme/resource/transaction.md +109 -0
- bitgen_sdk-1.0.0/readme/resource/webhooks.md +311 -0
- bitgen_sdk-1.0.0/src/bitgen/__init__.py +18 -0
- bitgen_sdk-1.0.0/src/bitgen/_http/__init__.py +1 -0
- bitgen_sdk-1.0.0/src/bitgen/_http/base_url.py +42 -0
- bitgen_sdk-1.0.0/src/bitgen/_http/client.py +131 -0
- bitgen_sdk-1.0.0/src/bitgen/_http/timeout.py +21 -0
- bitgen_sdk-1.0.0/src/bitgen/_http/transport.py +113 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/__init__.py +1 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/amount.py +42 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/asset_id.py +18 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/constants.py +19 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/path.py +17 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/reference.py +19 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/user_id.py +26 -0
- bitgen_sdk-1.0.0/src/bitgen/_support/values.py +55 -0
- bitgen_sdk-1.0.0/src/bitgen/client.py +109 -0
- bitgen_sdk-1.0.0/src/bitgen/constants.py +36 -0
- bitgen_sdk-1.0.0/src/bitgen/errors.py +35 -0
- bitgen_sdk-1.0.0/src/bitgen/models/__init__.py +197 -0
- bitgen_sdk-1.0.0/src/bitgen/models/_cast.py +161 -0
- bitgen_sdk-1.0.0/src/bitgen/models/apikeys.py +136 -0
- bitgen_sdk-1.0.0/src/bitgen/models/asset.py +205 -0
- bitgen_sdk-1.0.0/src/bitgen/models/bank.py +104 -0
- bitgen_sdk-1.0.0/src/bitgen/models/core.py +122 -0
- bitgen_sdk-1.0.0/src/bitgen/models/custody.py +148 -0
- bitgen_sdk-1.0.0/src/bitgen/models/customer.py +598 -0
- bitgen_sdk-1.0.0/src/bitgen/models/history.py +44 -0
- bitgen_sdk-1.0.0/src/bitgen/models/organization.py +42 -0
- bitgen_sdk-1.0.0/src/bitgen/models/staking.py +229 -0
- bitgen_sdk-1.0.0/src/bitgen/models/trading.py +137 -0
- bitgen_sdk-1.0.0/src/bitgen/models/transaction.py +165 -0
- bitgen_sdk-1.0.0/src/bitgen/models/webhooks.py +237 -0
- bitgen_sdk-1.0.0/src/bitgen/page.py +42 -0
- bitgen_sdk-1.0.0/src/bitgen/py.typed +0 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/__init__.py +27 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/apikeys.py +50 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/asset.py +37 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/bank.py +103 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/core.py +42 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/custody.py +86 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/customer.py +133 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/staking.py +129 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/trading.py +100 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/transaction.py +50 -0
- bitgen_sdk-1.0.0/src/bitgen/resources/webhooks.py +237 -0
- bitgen_sdk-1.0.0/src/bitgen/version.py +4 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## [1.0.0] - 2026-09-19
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- `BitgenClient` with keyword-only arguments (`scope`, `apiKey`, `env`, `host`, `port`, `isSsl`, `timeout`); `env` is one of the `bitgen.Env` constants
|
|
5
|
+
- `Env` (`PRODUCTION`, `SANDBOX`) and `Asset` (`BTC`, `ETH`, `USDC`, `XRP`, `SOL` — provisional list) string constants: every enumerated value is a constant on a frozen class listing its `VALUES`
|
|
6
|
+
- `BitgenError` with `status` (the HTTP status) and `code` (the stable error code of the API); `str(error)` is `"<code> (HTTP <status>)"`
|
|
7
|
+
- `request_timeout` / `network_error` errors (`status` `0`, transport error in `__cause__`)
|
|
8
|
+
- `timeout` option, in seconds (default `30`, `0` disables), bounding the whole request
|
|
9
|
+
- Invalid arguments raise a `ValueError` (or a `TypeError` on a wrong type) before any request
|
|
10
|
+
- `User-Agent: bitgen-sdk-python/<version>` header on every request
|
|
11
|
+
- `Page[T]` for paginated lists (`count`, `items`); `UnexpectedAnswerError` for a 2xx answer that is not the shape the API promises
|
|
12
|
+
- `customer` resource (`create` with the `needActivation` / `notify` options, `list`, `get`, `update`) and its models — `Identity` as `KycIdentity` / `KybIdentity`; `UserRef` (`str | Created | Customer | Account | UserSummary | OrderUser`) wherever a customer is expected
|
|
13
|
+
- `bank` resource (`get`, `operations`, `withdraw`, `credit`) and its models — amounts as `str`, `int`, `float` or `Decimal`
|
|
14
|
+
- `custody` resource (`wallets`, `wallet`, `portfolio`, `withdraw` with `TravelRulePerson` / `TravelRulePlatform`) and its models
|
|
15
|
+
- `trading` resource (`buy`, `sell`, `get`, `list`) and its models
|
|
16
|
+
- `transaction` resource (`list`, `get`) and its models
|
|
17
|
+
- `staking` resource (`providers`, `stake`, `list`, `movements`, `get`, `rewards`, `unstake`, `operations`, `portfolio`) and its models
|
|
18
|
+
- `core` resource (`list`, `get`) and its models
|
|
19
|
+
- `webhooks` resource (`activate`, `updateEndpoint`, `regenerate`, `list`, `subscribe`, `archive`, `reactivate`, `logs`, `catalog`, `catalogItem`) and its models, with `verify()` to check a received delivery (HMAC signature, freshness, envelope) — the headers as any `dict`, WSGI `environ`, framework `headers` object or list of `(name, value)` pairs
|
|
20
|
+
- `apikeys` resource (`list`, `get`, `logs`) and its models
|
|
21
|
+
- Every method that expects the uuid of an object the SDK returns also takes the model itself (`Order`, `StakingMovement`, `StakingPosition`, `Core`, `Transaction`, `Subscriber`, `WebhookType`, `Apikey`)
|
|
22
|
+
- `asset` resource (`list`, `get`, `tickers`, `ticker`) and its models under `bitgen.models` (`Asset`, `AssetTicker`, `AssetFees`, `AssetNetwork`…, the `AssetState` constants, the shared `History`); every method that expects an asset also takes an `Asset` / `AssetRef` model
|
|
23
|
+
- No dependency beyond the standard library; Python 3.11 or later; fully typed (`py.typed`)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bitgen-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for the BITGEN API v4
|
|
5
|
+
Project-URL: Homepage, https://github.com/bitgenplatform/sdk-python
|
|
6
|
+
Project-URL: Repository, https://github.com/bitgenplatform/sdk-python
|
|
7
|
+
Project-URL: Changelog, https://github.com/bitgenplatform/sdk-python/blob/main/CHANGELOG.md
|
|
8
|
+
Author: BITGEN
|
|
9
|
+
License-Expression: LicenseRef-Proprietary
|
|
10
|
+
Keywords: bitgen,crypto,sdk
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: build<2,>=1.6; extra == 'dev'
|
|
24
|
+
Requires-Dist: mypy<1.21,>=1.20; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest<9,>=8.4; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == 'dev'
|
|
27
|
+
Requires-Dist: twine<8,>=7.0; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# bitgen-sdk — v1.0.0
|
|
31
|
+
|
|
32
|
+
Official Python SDK for the BITGEN API v4 — server-side, Python 3.11+, no dependency beyond the standard library.
|
|
33
|
+
Install it with `pip install bitgen-sdk`.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from bitgen import BitgenClient, Env
|
|
37
|
+
|
|
38
|
+
client = BitgenClient(
|
|
39
|
+
scope="YOUR_SCOPE_UUID", # uuid of the organization that owns the key
|
|
40
|
+
apiKey="YOUR_API_KEY",
|
|
41
|
+
env=Env.SANDBOX, # Env.PRODUCTION by default
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- [Installation](readme/installation.md) — Python 3.11+, pip, what to import
|
|
46
|
+
- [Quick start](readme/quick-start.md) — a customer, their EUR account, a wallet, a purchase
|
|
47
|
+
- [Configuration](readme/configuration.md) — credentials, environments, custom host, timeout
|
|
48
|
+
- [Concepts](readme/concepts.md) — user references, amounts, pagination, assets, activation, the flows of a purchase, a sale, a deposit and a withdrawal
|
|
49
|
+
- [Errors](readme/errors.md) — `BitgenError`, error codes, invalid arguments
|
|
50
|
+
|
|
51
|
+
Resources, in the order of an integration:
|
|
52
|
+
|
|
53
|
+
- [Customers](readme/resource/customer.md) — `client.customer`
|
|
54
|
+
- [Bank accounts](readme/resource/bank.md) — `client.bank`
|
|
55
|
+
- [Custody wallets](readme/resource/custody.md) — `client.custody`
|
|
56
|
+
- [Trading](readme/resource/trading.md) — `client.trading`
|
|
57
|
+
- [Transactions](readme/resource/transaction.md) — `client.transaction`
|
|
58
|
+
- [Staking](readme/resource/staking.md) — `client.staking`
|
|
59
|
+
- [Connectors](readme/resource/core.md) — `client.core`
|
|
60
|
+
- [Webhooks](readme/resource/webhooks.md) — `client.webhooks`
|
|
61
|
+
- [API keys](readme/resource/apikeys.md) — `client.apikeys`
|
|
62
|
+
- [Assets](readme/resource/asset.md) — `client.asset`
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
Private — © BITGEN
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# bitgen-sdk — v1.0.0
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the BITGEN API v4 — server-side, Python 3.11+, no dependency beyond the standard library.
|
|
4
|
+
Install it with `pip install bitgen-sdk`.
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
from bitgen import BitgenClient, Env
|
|
8
|
+
|
|
9
|
+
client = BitgenClient(
|
|
10
|
+
scope="YOUR_SCOPE_UUID", # uuid of the organization that owns the key
|
|
11
|
+
apiKey="YOUR_API_KEY",
|
|
12
|
+
env=Env.SANDBOX, # Env.PRODUCTION by default
|
|
13
|
+
)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- [Installation](readme/installation.md) — Python 3.11+, pip, what to import
|
|
17
|
+
- [Quick start](readme/quick-start.md) — a customer, their EUR account, a wallet, a purchase
|
|
18
|
+
- [Configuration](readme/configuration.md) — credentials, environments, custom host, timeout
|
|
19
|
+
- [Concepts](readme/concepts.md) — user references, amounts, pagination, assets, activation, the flows of a purchase, a sale, a deposit and a withdrawal
|
|
20
|
+
- [Errors](readme/errors.md) — `BitgenError`, error codes, invalid arguments
|
|
21
|
+
|
|
22
|
+
Resources, in the order of an integration:
|
|
23
|
+
|
|
24
|
+
- [Customers](readme/resource/customer.md) — `client.customer`
|
|
25
|
+
- [Bank accounts](readme/resource/bank.md) — `client.bank`
|
|
26
|
+
- [Custody wallets](readme/resource/custody.md) — `client.custody`
|
|
27
|
+
- [Trading](readme/resource/trading.md) — `client.trading`
|
|
28
|
+
- [Transactions](readme/resource/transaction.md) — `client.transaction`
|
|
29
|
+
- [Staking](readme/resource/staking.md) — `client.staking`
|
|
30
|
+
- [Connectors](readme/resource/core.md) — `client.core`
|
|
31
|
+
- [Webhooks](readme/resource/webhooks.md) — `client.webhooks`
|
|
32
|
+
- [API keys](readme/resource/apikeys.md) — `client.apikeys`
|
|
33
|
+
- [Assets](readme/resource/asset.md) — `client.asset`
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
Private — © BITGEN
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
# hatchling 1.27 brings PEP 639 (the license expression); 1.32 starts emitting Metadata 2.5 — 2.4 until PyPI is known to take it
|
|
3
|
+
requires = ["hatchling>=1.27,<1.32"]
|
|
4
|
+
build-backend = "hatchling.build"
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "bitgen-sdk"
|
|
8
|
+
dynamic = ["version"]
|
|
9
|
+
description = "Official Python SDK for the BITGEN API v4"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
license = "LicenseRef-Proprietary"
|
|
13
|
+
authors = [{ name = "BITGEN" }]
|
|
14
|
+
keywords = ["bitgen", "crypto", "sdk"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/bitgenplatform/sdk-python"
|
|
31
|
+
Repository = "https://github.com/bitgenplatform/sdk-python"
|
|
32
|
+
Changelog = "https://github.com/bitgenplatform/sdk-python/blob/main/CHANGELOG.md"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=8.4,<9",
|
|
37
|
+
"mypy>=1.20,<1.21",
|
|
38
|
+
"ruff>=0.16,<0.17",
|
|
39
|
+
"build>=1.6,<2",
|
|
40
|
+
"twine>=7.0,<8",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.version]
|
|
44
|
+
path = "src/bitgen/version.py"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.sdist]
|
|
47
|
+
only-include = ["src/bitgen", "readme", "README.md", "CHANGELOG.md", "pyproject.toml"]
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.wheel]
|
|
50
|
+
packages = ["src/bitgen"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
filterwarnings = ["error"]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
files = ["src", "tests"]
|
|
58
|
+
python_version = "3.11"
|
|
59
|
+
strict = true
|
|
60
|
+
warn_unreachable = true
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 120
|
|
64
|
+
target-version = "py311"
|
|
65
|
+
src = ["src", "tests"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint]
|
|
68
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF", "PL", "PERF", "RET", "T20", "S"]
|
|
69
|
+
ignore = ["PLR2004", "PLR0913", "PLR0917"]
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint.per-file-ignores]
|
|
72
|
+
# tests: assert is the point, subprocess runs the doc examples and openssl with our own arguments, pickle our own
|
|
73
|
+
# errors, the webhook secrets are fixtures
|
|
74
|
+
"tests/**" = ["T20", "S101", "S603", "S301", "S105"]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
The conventions shared by every resource of the SDK: how a customer is designated, how amounts, pages, booleans, assets and dates travel, and which customers the financial resources accept. Examples use `client`, a configured `BitgenClient` ([Configuration](configuration.md)), and `customer`, the `Created` returned by `client.customer.create()`.
|
|
4
|
+
|
|
5
|
+
## User references
|
|
6
|
+
|
|
7
|
+
Wherever a method expects a customer, its signature says `UserRef` — a type alias of `bitgen.models`: `str | Created | Customer | Account | UserSummary | OrderUser`. A string is the customer's **uuid**; a model is one that carries it. The SDK sends the uuid of the model: the `Created` returned by `client.customer.create()`, a `Customer` of `client.customer.list()`, an `Account` of `client.customer.get()`, the `user` of an `Order`, the `owner` of a `Transaction` or of a `StakingMovement`.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
account = client.bank.get("CUSTOMER_UUID")
|
|
11
|
+
account = client.bank.get(customer) # the Created returned by client.customer.create()
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Any other object — a `Wallet`, an `Order`, a dict with a `uuid` — is a `TypeError` at the call, before any request. An email works too where the API resolves it (`customer`, `bank`, `custody`, `staking` — not `trading`, nor the `user` filter of the lists), but the uuid is cheaper for the API — prefer it.
|
|
15
|
+
|
|
16
|
+
The same goes for the other objects the SDK returns: wherever a method expects the uuid of an order, a movement, a position, a connector, a transaction, a subscription, an event of the catalogue or a key, it also takes the model itself (`client.staking.rewards(movement.staking)`, `client.trading.get(order)`), and sends its uuid.
|
|
17
|
+
|
|
18
|
+
## Amounts
|
|
19
|
+
|
|
20
|
+
The API handles crypto amounts as **strings** (up to 18 decimals): a Python `float` only keeps about 15 significant digits and nothing on the server side restores what it lost. The SDK therefore:
|
|
21
|
+
|
|
22
|
+
- accepts a `str`, an `int`, a `float` or a `Decimal` and always sends it as a string — a string is sent as is, an `int` or a `float` in its shortest decimal form, a `Decimal` in plain notation;
|
|
23
|
+
- refuses, with a `ValueError` and before any request, an empty string, a negative or non-finite number, and a `float` Python would write in exponent notation — below `0.0001` or from `1e16`: pass those as strings or as `Decimal`;
|
|
24
|
+
- never rounds or reformats a string: `"0.000000000000000001"` reaches the API untouched.
|
|
25
|
+
|
|
26
|
+
**Prefer strings or `Decimal`**, even for EUR: `0.1 + 0.2` is `0.30000000000000004` as a `float`. In responses, crypto quantities are strings and EUR amounts are `float`s with 2 decimals.
|
|
27
|
+
|
|
28
|
+
**Minimums.** Purchases, sales, on-chain withdrawals and staking movements have minimums — set by BITGEN per environment, subject to change, never hard-coded in the SDK. The API's answer is the source of truth: `416 invalid_amount` for a purchase or a sale, `416 withdraw_below_minimum` for an on-chain withdrawal, `422 amount_below_minimum` for a staking movement. For staking, the provider's minimums are readable in its configuration (`client.staking.providers()`, [Staking](resource/staking.md#providers)).
|
|
29
|
+
|
|
30
|
+
## Pagination
|
|
31
|
+
|
|
32
|
+
Paginated lists take `offset` and `limit` (`limit`: default 10, max 50; the transaction journal accepts up to 100) and return a `bitgen.Page`:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
page = client.asset.list()
|
|
36
|
+
|
|
37
|
+
page.count # the total number of items
|
|
38
|
+
page.items # the items of this page, typed models — Page[models.Asset] here
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`Page[T]` is generic: `page.items` is a `list[T]`, and a type checker knows what each item is.
|
|
42
|
+
|
|
43
|
+
## Query booleans
|
|
44
|
+
|
|
45
|
+
The boolean filters of the lists (`includeClosed`, `includeRevoked`, `includeArchived`) are sent as `true` / `false`. The API also reads `1` / `0`, treats an absent parameter as `false`, and answers `422 invalid_<param>` for any other value: `invalid_include_closed`, `invalid_include_revoked`, `invalid_include_archived`.
|
|
46
|
+
|
|
47
|
+
## Assets
|
|
48
|
+
|
|
49
|
+
Wherever an asset is expected, the SDK accepts its **uuid** or its **ISO code** as a string, in any case (the API normalizes it) — or an `Asset` / `AssetRef` model returned by the SDK, whose uuid is then sent. The `bitgen.Asset` constants are the ISO codes of the main assets; any other code known to the catalogue ([Assets](resource/asset.md)) is passed as a plain string.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from bitgen import Asset
|
|
53
|
+
|
|
54
|
+
Asset.BTC # "btc"
|
|
55
|
+
Asset.ETH # "eth"
|
|
56
|
+
Asset.USDC # "usdc"
|
|
57
|
+
Asset.XRP # "xrp"
|
|
58
|
+
Asset.SOL # "sol"
|
|
59
|
+
Asset.VALUES # ("btc", "eth", "usdc", "xrp", "sol")
|
|
60
|
+
|
|
61
|
+
eth = client.asset.get(Asset.ETH) # or by uuid
|
|
62
|
+
wallet = client.custody.wallet(customer, eth) # the model: its uuid is sent
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The `iso` the API returns has the case it is stored with (`ETH` today): **compare it case-insensitively**. `bitgen.Asset` holds the ISO codes; the asset returned by `client.asset` is the `bitgen.models.Asset` model.
|
|
66
|
+
|
|
67
|
+
## Constants
|
|
68
|
+
|
|
69
|
+
The SDK has no enums: every value the API enumerates is a **string constant** on a small class — `Env.SANDBOX` is `"sandbox"`, `Locale.FR` is `"FR"`, `TradingDirection.BUY` is `"buy"`, `AssetState.AVAILABLE` is `"AVAILABLE"`, `WebhookEventName.CUSTODY_SENT` is `"custody.sent"` — and each class lists its values in `VALUES`, in the order of the API. The classes of the values a model carries live under `bitgen.models`, next to the model.
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from bitgen import Asset
|
|
73
|
+
from bitgen.models import AssetState, Locale
|
|
74
|
+
|
|
75
|
+
eth = client.asset.get(Asset.ETH)
|
|
76
|
+
if eth.state == AssetState.AVAILABLE: # outputs are strings: compare them with the constants
|
|
77
|
+
client.customer.update(customer, locale=Locale.EN) # inputs take the constant
|
|
78
|
+
print(", ".join(Locale.VALUES)) # FR, EN
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
An input that is not one of the values (`locale="en"`, `direction="Buy"`) is refused with a `ValueError` before any request: the case matters. An output the SDK does not know yet (a state the API added) is kept as is, as a string. The constants cannot be changed or instantiated.
|
|
82
|
+
|
|
83
|
+
## Timestamps and histories
|
|
84
|
+
|
|
85
|
+
Timestamps are **epochs in seconds** (`createdAt`, `updatedAt`, `date`, `expiresAt`…). Time series are a `bitgen.models.History` with five lists of points, `d`, `w`, `m`, `y` and `all`:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from datetime import UTC, datetime
|
|
89
|
+
|
|
90
|
+
from bitgen import Asset
|
|
91
|
+
|
|
92
|
+
btc = client.asset.ticker(Asset.BTC)
|
|
93
|
+
|
|
94
|
+
for epoch, price in btc.history.d: # the last 24 hours, one point per hour
|
|
95
|
+
print(datetime.fromtimestamp(epoch, tz=UTC).strftime("%H:%M"), price)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Each point is a tuple `(epoch seconds, value)`: `d` covers the last 24 hours with one point per hour, `w` and `m` one point per day, `y` and `all` one point per month; the last point is the current value. Histories are the EUR price of an asset (`client.asset`), the EUR balance of a bank account (`client.bank`), the EUR value of a wallet or of a whole custody (`client.custody`), and the capital and revenues of a staking portfolio (`client.staking`).
|
|
99
|
+
|
|
100
|
+
## Activation and identity
|
|
101
|
+
|
|
102
|
+
By default, creating a customer sends them an activation email. Until they click it, the account stays `CREATED` and the **financial resources do not see it**: the bank answers `404 unknown_bank`, custody and staking `403 org_forbidden`, trading `403 user_not_in_scope`. Only the customer resource sees it (where the customer appears as `CREATED`). An organization that handles onboarding itself creates its customers with `needActivation=False` — usable right away, no BITGEN email — and `notify=False` for no BITGEN emails at all.
|
|
103
|
+
|
|
104
|
+
If your organization uses BITGEN's identity verification, the customer's identity (KYC for a person, KYB for a business) must be validated first — `412 owner_identity_not_validated` when reading the EUR account, `403 kyc_not_validated` on custody, trading and staking otherwise. An organization that verifies the identity of its customers by its own means has no such requirement. The verification itself is not part of the SDK; its state is the `state` of the customer's identity.
|
|
105
|
+
|
|
106
|
+

|
|
107
|
+
|
|
108
|
+
## Following a purchase and a sale
|
|
109
|
+
|
|
110
|
+
A purchase moves money across four resources. Placing the order reserves the EUR on the customer's bank account — an operation `PURCHASE` ([Bank accounts](resource/bank.md)); the exchange of the platform executes it — `EXECUTING`, then `FILLED` with the price, the fee and the quantity received ([Trading](resource/trading.md)); the quantity is delivered to the customer's custody wallet — `DELIVERING`, then `DONE` ([Custody wallets](resource/custody.md)) — where it appears as a custody transaction `IN` ([Transactions](resource/transaction.md)). Afterwards, the order carries the result, the operations of the bank account show the debit, the journal the delivery, the wallet its new balance, and the event `trading.buy` is sent ([Webhooks](resource/webhooks.md)).
|
|
111
|
+
|
|
112
|
+

|
|
113
|
+
|
|
114
|
+
A sale goes the other way. The quantity leaves the customer's custody wallet through an internal transfer to the exchange — a `silent` custody transaction `OUT` in the journal ([Custody wallets](resource/custody.md), [Transactions](resource/transaction.md)); the exchange executes it — `EXECUTING`, then `FILLED` at `executedPrice`, minus `fee` ([Trading](resource/trading.md)); the EUR received are credited on the customer's EUR account — `DONE`, an operation `SELL` and the event `bank.credited` ([Bank accounts](resource/bank.md)); the event `trading.sell` is sent ([Webhooks](resource/webhooks.md)).
|
|
115
|
+
|
|
116
|
+

|
|
117
|
+
|
|
118
|
+
## Following a deposit and a withdrawal
|
|
119
|
+
|
|
120
|
+
The EUR of a customer are held by the bank provider of your organization, on the organization's account — one IBAN, the same for all your customers, that you give them yourself. BITGEN holds no funds: it keeps the **ledger** of each customer — `balance`, `pending`, `history`, operations ([Bank accounts](resource/bank.md)). The provider receives the wires and pays the withdrawals; you read the ledger and receive the events ([Webhooks](resource/webhooks.md)).
|
|
121
|
+
|
|
122
|
+

|
|
123
|
+
|
|
124
|
+
A deposit: the customer wires EUR to the organization's account with the reference `BTGN` followed by the code of their account (`message`) → the provider reports the wire to BITGEN — with a manual bank, you declare it ([credit](resource/bank.md#credit)) → BITGEN matches the reference and the amount enters `pending.in_` → compliance analysis: a `BANK` `IN` transaction, `bank.transaction` (`PENDING` if an alert holds it — [Transactions](resource/transaction.md)) → `balance` credited, operation `DEPOSIT`, `bank.credited`. The credit is never immediate; without a valid reference the wire is never credited, and the compliance of your organization is notified.
|
|
125
|
+
|
|
126
|
+

|
|
127
|
+
|
|
128
|
+
A withdrawal: you request it ([Withdraw](resource/bank.md#withdraw)) — the amount is reserved in `pending.out`, `balance` untouched, `transaction` identifies the withdrawal → compliance analysis, `bank.transaction` → the provider wires from the organization's account to the customer's IBAN → at confirmation, `balance` debited, operation `WITHDRAWAL`, `bank.debited` with `amount`, `fee` and `net`. If it fails, the reserve is released.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
A `BitgenClient` is built once per API key and reused — it can be shared between threads: it holds the credentials, the target environment and the request timeout, and nothing else. Its arguments are keyword-only.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from bitgen import BitgenClient, Env
|
|
7
|
+
|
|
8
|
+
client = BitgenClient(
|
|
9
|
+
scope="YOUR_SCOPE_UUID",
|
|
10
|
+
apiKey="YOUR_API_KEY",
|
|
11
|
+
env=Env.PRODUCTION, # default
|
|
12
|
+
timeout=30, # seconds, default 30
|
|
13
|
+
)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Credentials
|
|
17
|
+
|
|
18
|
+
| Argument | Description |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `scope` | uuid of the organization that owns the key. Sent as the `BITGEN-Scope` header. It is also the organization the SDK uses wherever the API expects yours. |
|
|
21
|
+
| `apiKey` | The raw key, shown once when it is created. Sent as the `Api-key` header. |
|
|
22
|
+
|
|
23
|
+
A missing key, an unknown, revoked or expired key, or a `scope` that is not the key's organization, is refused with a `401` ([Common errors](errors.md#common-errors)).
|
|
24
|
+
|
|
25
|
+
## Environments
|
|
26
|
+
|
|
27
|
+
| `env` | Constant | URL |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `production` | `Env.PRODUCTION` (default) | `https://api.bitgen.com` |
|
|
30
|
+
| `sandbox` | `Env.SANDBOX` | `https://api.sandbox.bitgen.com` |
|
|
31
|
+
|
|
32
|
+
`Env` holds these names as constants (`Env.VALUES` lists them): pass the constant. Anything else is refused before any request ([Validation](#validation)).
|
|
33
|
+
|
|
34
|
+
## Custom host
|
|
35
|
+
|
|
36
|
+
To reach the API through another hostname — a container, a tunnel — give `host` instead of `env`:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from bitgen import BitgenClient
|
|
40
|
+
|
|
41
|
+
client = BitgenClient(
|
|
42
|
+
scope="YOUR_SCOPE_UUID",
|
|
43
|
+
apiKey="YOUR_API_KEY",
|
|
44
|
+
host="my-hostname", # bare hostname: no scheme, port or path
|
|
45
|
+
port=8080, # default 80
|
|
46
|
+
isSsl=False, # default True (https)
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`isSsl=False` sends the key unencrypted: only towards a local container or a tunnel, never across a network.
|
|
51
|
+
|
|
52
|
+
## Timeout
|
|
53
|
+
|
|
54
|
+
`timeout` is the maximum time, in seconds, the SDK waits for the API to answer — the whole request, from the connection to the last byte of the answer: `30` by default, `0` disables it; an `int` or a `float` (`0.5`). When it expires, the call raises a `BitgenError` with `status` `0` and `code` `request_timeout` ([No HTTP response](errors.md#no-http-response)).
|
|
55
|
+
|
|
56
|
+
## Requests
|
|
57
|
+
|
|
58
|
+
Every request carries the headers `BITGEN-Scope`, `Api-key`, `Content-Type: application/json`, `Accept: application/json` and `User-Agent: bitgen-sdk-python/<version>`, where `<version>` is the installed version of the SDK. TLS certificates are verified against the certificates of the system — or against those named by the `SSL_CERT_FILE` / `SSL_CERT_DIR` environment variables of OpenSSL, for a private certificate authority. Redirects are never followed. The SDK connects directly: it does not read the proxy variables of the environment.
|
|
59
|
+
|
|
60
|
+
## Validation
|
|
61
|
+
|
|
62
|
+
An invalid configuration raises a `ValueError` from the constructor — or a `TypeError` when an argument has the wrong type — before any request is sent: empty `scope` or `apiKey` (or one that is not printable ASCII), `env` that is not one of `Env.VALUES`, `host` that is not a bare hostname, `port` outside 1–65535, `timeout` that is not a number of seconds between `0` and `2147483`. The values of `scope`, `apiKey`, `env` and `host` never appear in the message. Every other invalid argument is refused the same way, by the method that receives it ([Invalid arguments](errors.md#invalid-arguments)).
|
|
63
|
+
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
| Argument | Type | Default | Description |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| `scope` | `str` | — | Organization uuid |
|
|
69
|
+
| `apiKey` | `str` | — | API key |
|
|
70
|
+
| `env` | `str` | `Env.PRODUCTION` | Target environment — an `Env` constant |
|
|
71
|
+
| `host` | `str \| None` | `None` | Custom hostname, used instead of `env` |
|
|
72
|
+
| `port` | `int \| None` | `80` | Port, with `host` |
|
|
73
|
+
| `isSsl` | `bool` | `True` | `https` or `http`, with `host` |
|
|
74
|
+
| `timeout` | `int \| float` | `30` | Request timeout in seconds, `0` = none |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Errors
|
|
2
|
+
|
|
3
|
+
The API answers with real HTTP status codes and, on failure, a JSON body `{ error: true, message: '<code>', code: <status> }` where `message` is a stable snake_case code (`invalid_amount`, `unknown_asset`…), never a sentence. The SDK turns every non-2xx answer — and every request that gets no HTTP answer at all — into a `BitgenError`.
|
|
4
|
+
|
|
5
|
+
## BitgenError
|
|
6
|
+
|
|
7
|
+
`bitgen.BitgenError` extends `Exception`:
|
|
8
|
+
|
|
9
|
+
| Member | Type | Description |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `status` | `int` | The HTTP status (`416`…), or `0` when no HTTP response was received |
|
|
12
|
+
| `code` | `str` | The stable code of the API (`requested_amount_error`), or the raw response text when the body is not the API's JSON error |
|
|
13
|
+
| `str(error)` | `str` | `"<code> (HTTP <status>)"` |
|
|
14
|
+
| `__cause__` | `BaseException \| None` | The transport error, for `request_timeout` and `network_error` — `None` otherwise |
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from bitgen import BitgenError
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
... # any call of the SDK
|
|
21
|
+
except BitgenError as error:
|
|
22
|
+
error.status # 416
|
|
23
|
+
error.code # "requested_amount_error"
|
|
24
|
+
str(error) # "requested_amount_error (HTTP 416)"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Nothing in the exception ever contains your API key: not the message, not the cause — even a raw response that echoes it back has the key redacted.
|
|
28
|
+
|
|
29
|
+
## No HTTP response
|
|
30
|
+
|
|
31
|
+
| Status | `code` | Meaning |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `0` | `request_timeout` | No response within the configured `timeout` — 30 seconds by default ([Timeout](configuration.md#timeout)) |
|
|
34
|
+
| `0` | `network_error` | The request never got an HTTP answer: DNS, connection refused, TLS… `__cause__` holds the transport error |
|
|
35
|
+
|
|
36
|
+
## Webhook verification
|
|
37
|
+
|
|
38
|
+
`client.webhooks.verify()` checks a delivery received by your endpoint locally, without any request: a delivery that fails the verification raises a `BitgenError` with `status` `0` and one of these codes ([Webhooks › verify](resource/webhooks.md#verify)):
|
|
39
|
+
|
|
40
|
+
| Status | `code` | Meaning |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `0` | `missing_signature` | No `X-BITGEN-Signature` header |
|
|
43
|
+
| `0` | `invalid_signature` | The signature does not match the body and the secret |
|
|
44
|
+
| `0` | `missing_timestamp` | No `X-BITGEN-Timestamp` header, or not a number |
|
|
45
|
+
| `0` | `timestamp_expired` | The timestamp of the delivery is more than `tolerance` seconds away from now (300 by default) |
|
|
46
|
+
| `0` | `invalid_payload` | The body is not the JSON envelope `{ delivery_id, timestamp, event, data }` |
|
|
47
|
+
|
|
48
|
+
## Unexpected answers
|
|
49
|
+
|
|
50
|
+
When the body is not the API's JSON error payload (proxy error page, unexpected `500`…), `code` holds the raw response text, truncated to 200 characters; the same goes for a 2xx answer that is not JSON. A `500` is not always a failure of the API: it is also its answer to a request it does not recognize — with a custom `host`, check that it reaches the API unchanged. Redirects are never followed: a `3xx` answer is reported as a `BitgenError`, and the key is never replayed to another host. A 2xx answer that is JSON but not the shape the SDK expects (not an object, not a list of objects where the API promises one, a page whose `items` is not a list…) raises a `bitgen.UnexpectedAnswerError`: not an error of the API, a contract violation to report to BITGEN. A value the SDK enumerates (a `state`, a `mode`…) is never checked: the API may add one, the models keep it as a string.
|
|
51
|
+
|
|
52
|
+
## Invalid arguments
|
|
53
|
+
|
|
54
|
+
An invalid argument — an empty `scope` or `apiKey`, a `host` with a scheme, a negative `timeout`, a value outside a constant list ([Constants](concepts.md#constants)), an empty asset… — raises a `ValueError` **before any request is sent**, from the constructor or from the method that receives it; an argument of the wrong type (a `port` given as a string) raises a `TypeError` the same way ([Validation](configuration.md#validation)).
|
|
55
|
+
|
|
56
|
+
## Common errors
|
|
57
|
+
|
|
58
|
+
The errors any call can answer.
|
|
59
|
+
|
|
60
|
+
| Status | `code` | Meaning |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `401` | `auth_missing` | The `BITGEN-Scope` or `Api-key` header is missing |
|
|
63
|
+
| `401` | `api_key_missmatch` | The key is unknown, revoked or expired, or `scope` is not the key's organization |
|
|
64
|
+
| `403` | `unknown_organization` | The organization of the key is unknown |
|
|
65
|
+
| `403` | `organization_not_enabled` | The organization is not enabled |
|
|
66
|
+
| `403` | `api_disabled` | API access is not enabled for the organization |
|
|
67
|
+
| `403` | `invalid_api_key` | The key is invalid |
|
|
68
|
+
| `403` | `expired_api_key` | The key has expired |
|
|
69
|
+
| `403` | `forbidden_permission` | The key does not carry the permission for this call — contact BITGEN |
|
|
70
|
+
| `400` | `required_index_missing::<field>` | A mandatory field is missing or empty |
|
|
71
|
+
| `404` | `unknown_<resource>` | The target does not exist — `unknown_user`, `unknown_asset`, `unknown_bank`… — or the API does not reveal it |
|
|
72
|
+
| `422` | `invalid_<param>` | A boolean filter (`includeClosed`, `includeRevoked`, `includeArchived`) is not a boolean value |
|
|
73
|
+
| `423` | `blocked_by_alert` | The customer is under an active compliance alert |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
`bitgen-sdk` is the official Python SDK for the BITGEN API v4. It runs server-side only: the API accepts browser requests from a fixed list of origins, so the SDK is not meant to be used from a browser.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Python 3.11 or later
|
|
8
|
+
- No other dependency: the SDK only uses what Python already provides
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install bitgen-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Import
|
|
17
|
+
|
|
18
|
+
The client, its errors and the constants are imported from the `bitgen` package:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from bitgen import Asset, BitgenClient, BitgenError, Env
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The package exposes the client (`BitgenClient`), its exception (`BitgenError`), two constant classes — `Env`, the environments, and `Asset`, the ISO codes of the main assets ([Configuration](configuration.md), [Assets](concepts.md#assets)) — and `Page`, the paginated lists. Under `bitgen.models` live the models the resources return, the constant classes naming their known values ([Constants](concepts.md#constants)), and the few objects a call takes as input (`TravelRulePerson`, `TravelRulePlatform`, the `ReceivedHeaders` shape of `verify`):
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from bitgen.models import AssetState, History
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Typing
|
|
31
|
+
|
|
32
|
+
The SDK is fully typed and ships its type information (`py.typed`), so a type checker such as mypy or pyright sees every signature: paginated lists are a generic `Page[T]` (`page.count`, `page.items`), and every value the API returns is an immutable object with typed attributes.
|
|
33
|
+
|
|
34
|
+
## Next steps
|
|
35
|
+
|
|
36
|
+
- [Quick start](quick-start.md) — create the client and run a first customer journey
|
|
37
|
+
- [Configuration](configuration.md) — credentials, environments, custom host, timeout
|
|
38
|
+
- [Concepts](concepts.md) — user references, amounts, pagination, assets, activation, the flows of a purchase, a sale, a deposit and a withdrawal
|
|
39
|
+
- [Errors](errors.md) — what a failed call raises, and what is checked before any request
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 560" role="img" aria-labelledby="title">
|
|
2
|
+
<title id="title">Activation and identity: from the creation of a customer to the financial resources</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<marker id="m94a3b8" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="8" markerHeight="8" markerUnits="userSpaceOnUse" orient="auto"><path d="M0,0.5 L7,4 L0,7.5 z" fill="#94a3b8"/></marker>
|
|
5
|
+
<pattern id="grid" width="16" height="16" patternUnits="userSpaceOnUse"><circle cx="8" cy="8" r="1" fill="#e2e8f0"/></pattern>
|
|
6
|
+
<filter id="shadow" x="-10%" y="-20%" width="120%" height="160%"><feDropShadow dx="0" dy="1" stdDeviation="2" flood-color="#0f172a" flood-opacity="0.10"/></filter>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="1000" height="560" fill="#ffffff"/>
|
|
9
|
+
<rect width="1000" height="560" fill="url(#grid)"/>
|
|
10
|
+
<text x="520.0" y="34.5" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="14" font-weight="600" fill="#0f172a" text-anchor="middle">Activation and identity</text>
|
|
11
|
+
<rect x="420" y="54" width="200" height="40" rx="10" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
12
|
+
<text x="520.0" y="78.7" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="500" fill="#0f172a" text-anchor="middle">Create a customer</text>
|
|
13
|
+
<path d="M520,95 V116" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
14
|
+
<rect x="455" y="118" width="130" height="24" rx="12.0" fill="#0f172a"/>
|
|
15
|
+
<text x="520.0" y="134.1" font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="11.5" font-weight="500" fill="#ffffff" text-anchor="middle">needActivation?</text>
|
|
16
|
+
<path d="M520,143 V178" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
17
|
+
<rect x="533" y="148" width="66" height="24" rx="12.0" fill="#f1f5f9"/>
|
|
18
|
+
<text x="566.0" y="164.1" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="500" fill="#475569" text-anchor="middle">default</text>
|
|
19
|
+
<rect x="360" y="180" width="320" height="56" rx="10" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
20
|
+
<text x="520.0" y="204.2" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="400" fill="#0f172a" text-anchor="middle">Activation email sent, account <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#4338ca">CREATED</tspan></text>
|
|
21
|
+
<text x="520.0" y="221.2" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="400" fill="#0f172a" text-anchor="middle">until the customer clicks it</text>
|
|
22
|
+
<path d="M520,237 V282" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
23
|
+
<rect x="529" y="247" width="54" height="24" rx="12.0" fill="#f1f5f9"/>
|
|
24
|
+
<text x="556.0" y="263.1" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="500" fill="#475569" text-anchor="middle">click</text>
|
|
25
|
+
<path d="M590,130 H790 Q800,130 800,140 V306 Q800,316 790,316 H612" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
26
|
+
<rect x="713" y="118" width="54" height="24" rx="12.0" fill="#f1f5f9"/>
|
|
27
|
+
<text x="740.0" y="134.1" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="500" fill="#475569" text-anchor="middle">false</text>
|
|
28
|
+
<rect x="430" y="296" width="180" height="40" rx="10" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
29
|
+
<text x="520.0" y="320.7" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="400" fill="#0f172a" text-anchor="middle">Account <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#4338ca">ENABLED</tspan></text>
|
|
30
|
+
<rect x="40" y="176" width="340" height="106" rx="10" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
31
|
+
<path d="M50,188 V270" stroke="#dc2626" stroke-width="3" stroke-linecap="round"/>
|
|
32
|
+
<text x="64.0" y="200.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="12" font-weight="600" fill="#0f172a" text-anchor="start">Meanwhile, only the customer resource sees it</text>
|
|
33
|
+
<text x="64.0" y="220.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="400" fill="#475569" text-anchor="start">bank: <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#dc2626">404 unknown_bank</tspan></text>
|
|
34
|
+
<text x="64.0" y="237.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="400" fill="#475569" text-anchor="start">custody, staking: <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#dc2626">403 org_forbidden</tspan></text>
|
|
35
|
+
<text x="64.0" y="254.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="400" fill="#475569" text-anchor="start">trading: <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#dc2626">403 user_not_in_scope</tspan></text>
|
|
36
|
+
<path d="M380,229 H358" fill="none" stroke="#cbd5e1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4 4"/>
|
|
37
|
+
<path d="M520,337 V358" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
38
|
+
<rect x="418" y="360" width="205" height="24" rx="12.0" fill="#0f172a"/>
|
|
39
|
+
<text x="520.0" y="376.1" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="500" fill="#ffffff" text-anchor="middle">BITGEN identity verification?</text>
|
|
40
|
+
<path d="M520,385 V420" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
41
|
+
<rect x="530" y="390" width="41" height="24" rx="12.0" fill="#f1f5f9"/>
|
|
42
|
+
<text x="550.0" y="406.1" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="500" fill="#475569" text-anchor="middle">yes</text>
|
|
43
|
+
<rect x="360" y="422" width="320" height="56" rx="10" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
44
|
+
<text x="520.0" y="446.2" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="400" fill="#0f172a" text-anchor="middle">Identity verification, KYC or KYB,</text>
|
|
45
|
+
<text x="520.0" y="463.2" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="400" fill="#0f172a" text-anchor="middle">until the identity is <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#4338ca">VALIDATED</tspan></text>
|
|
46
|
+
<path d="M520,479 V510" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
47
|
+
<path d="M620,372 H790 Q800,372 800,382 V520 Q800,530 790,530 H672" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#m94a3b8)"/>
|
|
48
|
+
<rect x="733" y="360" width="35" height="24" rx="12.0" fill="#f1f5f9"/>
|
|
49
|
+
<text x="750.0" y="376.1" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="500" fill="#475569" text-anchor="middle">no</text>
|
|
50
|
+
<rect x="40" y="412" width="340" height="88" rx="10" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
51
|
+
<path d="M50,424 V488" stroke="#dc2626" stroke-width="3" stroke-linecap="round"/>
|
|
52
|
+
<text x="64.0" y="436.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="12" font-weight="600" fill="#0f172a" text-anchor="start">Before that</text>
|
|
53
|
+
<text x="64.0" y="456.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="400" fill="#475569" text-anchor="start">EUR account: <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#dc2626">412 owner_identity_not_validated</tspan></text>
|
|
54
|
+
<text x="64.0" y="473.0" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="11.5" font-weight="400" fill="#475569" text-anchor="start">custody, trading, staking: <tspan font-family="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="0.92em" font-weight="600" fill="#dc2626">403 kyc_not_validated</tspan></text>
|
|
55
|
+
<path d="M380,456 H358" fill="none" stroke="#cbd5e1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4 4"/>
|
|
56
|
+
<rect x="370" y="510" width="300" height="40" rx="10" fill="#dcfce7" stroke="#bbf7d0" filter="url(#shadow)"/>
|
|
57
|
+
<text x="520.0" y="534.7" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="600" fill="#15803d" text-anchor="middle">Bank account, custody, trading and staking</text>
|
|
58
|
+
</svg>
|