foil-server 0.2.3__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.
Files changed (63) hide show
  1. foil_server-0.2.3/.github/workflows/ci.yml +56 -0
  2. foil_server-0.2.3/.github/workflows/release.yml +142 -0
  3. foil_server-0.2.3/.gitignore +7 -0
  4. foil_server-0.2.3/LICENSE +21 -0
  5. foil_server-0.2.3/PKG-INFO +196 -0
  6. foil_server-0.2.3/README.md +181 -0
  7. foil_server-0.2.3/RELEASING.md +35 -0
  8. foil_server-0.2.3/foil_server/__init__.py +130 -0
  9. foil_server-0.2.3/foil_server/client.py +1032 -0
  10. foil_server-0.2.3/foil_server/errors.py +26 -0
  11. foil_server-0.2.3/foil_server/gate_delivery.py +444 -0
  12. foil_server-0.2.3/foil_server/sealed_token.py +121 -0
  13. foil_server-0.2.3/foil_server/types.py +488 -0
  14. foil_server-0.2.3/pyproject.toml +26 -0
  15. foil_server-0.2.3/scripts/check-spec-sync.sh +68 -0
  16. foil_server-0.2.3/spec/LICENSE +21 -0
  17. foil_server-0.2.3/spec/README.md +160 -0
  18. foil_server-0.2.3/spec/fixtures/api/fingerprints/detail.json +70 -0
  19. foil_server-0.2.3/spec/fixtures/api/fingerprints/list.json +37 -0
  20. foil_server-0.2.3/spec/fixtures/api/gate/agent-token-revoke.json +3 -0
  21. foil_server-0.2.3/spec/fixtures/api/gate/agent-token-verify.json +12 -0
  22. foil_server-0.2.3/spec/fixtures/api/gate/login-session-consume.json +10 -0
  23. foil_server-0.2.3/spec/fixtures/api/gate/login-session-create.json +12 -0
  24. foil_server-0.2.3/spec/fixtures/api/gate/registry-detail.json +45 -0
  25. foil_server-0.2.3/spec/fixtures/api/gate/registry-list.json +47 -0
  26. foil_server-0.2.3/spec/fixtures/api/gate/service-create.json +49 -0
  27. foil_server-0.2.3/spec/fixtures/api/gate/service-detail.json +49 -0
  28. foil_server-0.2.3/spec/fixtures/api/gate/service-disable.json +49 -0
  29. foil_server-0.2.3/spec/fixtures/api/gate/service-update.json +49 -0
  30. foil_server-0.2.3/spec/fixtures/api/gate/services-list.json +51 -0
  31. foil_server-0.2.3/spec/fixtures/api/gate/session-ack.json +10 -0
  32. foil_server-0.2.3/spec/fixtures/api/gate/session-create.json +13 -0
  33. foil_server-0.2.3/spec/fixtures/api/gate/session-poll.json +36 -0
  34. foil_server-0.2.3/spec/fixtures/api/organizations/api-key-create.json +27 -0
  35. foil_server-0.2.3/spec/fixtures/api/organizations/api-key-list.json +31 -0
  36. foil_server-0.2.3/spec/fixtures/api/organizations/api-key-revoke.json +25 -0
  37. foil_server-0.2.3/spec/fixtures/api/organizations/api-key-rotate.json +27 -0
  38. foil_server-0.2.3/spec/fixtures/api/organizations/api-key-update.json +29 -0
  39. foil_server-0.2.3/spec/fixtures/api/organizations/organization-create.json +14 -0
  40. foil_server-0.2.3/spec/fixtures/api/organizations/organization-update.json +14 -0
  41. foil_server-0.2.3/spec/fixtures/api/organizations/organization.json +14 -0
  42. foil_server-0.2.3/spec/fixtures/api/sessions/detail.json +434 -0
  43. foil_server-0.2.3/spec/fixtures/api/sessions/list.json +36 -0
  44. foil_server-0.2.3/spec/fixtures/errors/invalid-api-key.json +10 -0
  45. foil_server-0.2.3/spec/fixtures/errors/missing-api-key.json +10 -0
  46. foil_server-0.2.3/spec/fixtures/errors/not-found.json +10 -0
  47. foil_server-0.2.3/spec/fixtures/errors/validation-error.json +20 -0
  48. foil_server-0.2.3/spec/fixtures/gate-delivery/approved-webhook-payload.valid.json +19 -0
  49. foil_server-0.2.3/spec/fixtures/gate-delivery/delivery-request.json +9 -0
  50. foil_server-0.2.3/spec/fixtures/gate-delivery/env-policy.json +40 -0
  51. foil_server-0.2.3/spec/fixtures/gate-delivery/vector.v1.json +28 -0
  52. foil_server-0.2.3/spec/fixtures/gate-delivery/webhook-signature.json +9 -0
  53. foil_server-0.2.3/spec/fixtures/manifest.json +185 -0
  54. foil_server-0.2.3/spec/fixtures/sealed-token/invalid.json +4 -0
  55. foil_server-0.2.3/spec/fixtures/sealed-token/vector.v1.json +54 -0
  56. foil_server-0.2.3/spec/openapi.json +20482 -0
  57. foil_server-0.2.3/spec/sealed-token.md +114 -0
  58. foil_server-0.2.3/tests/test_client.py +444 -0
  59. foil_server-0.2.3/tests/test_contract.py +179 -0
  60. foil_server-0.2.3/tests/test_gate_delivery.py +129 -0
  61. foil_server-0.2.3/tests/test_helpers.py +11 -0
  62. foil_server-0.2.3/tests/test_live.py +104 -0
  63. foil_server-0.2.3/tests/test_sealed_token.py +36 -0
@@ -0,0 +1,56 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - codex/**
8
+ pull_request:
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ test:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+ - uses: actions/setup-python@v6
20
+ with:
21
+ python-version: "3.12"
22
+ - run: python -m pip install --upgrade pip build
23
+ - run: python -m pip install -e .
24
+ - run: python -m unittest tests.test_client tests.test_contract tests.test_sealed_token
25
+ - run: python -m build
26
+
27
+ spec-sync:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v6
31
+ - name: Check synced spec
32
+ run: bash ./scripts/check-spec-sync.sh
33
+
34
+ live-smoke:
35
+ runs-on: ubuntu-latest
36
+ needs: [test, spec-sync]
37
+ if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }}
38
+ env:
39
+ FOIL_LIVE_SMOKE: "1"
40
+ FOIL_SMOKE_SECRET_KEY: ${{ secrets.FOIL_SMOKE_SECRET_KEY }}
41
+ FOIL_SMOKE_ORGANIZATION_ID: ${{ secrets.FOIL_SMOKE_ORGANIZATION_ID }}
42
+ FOIL_SMOKE_BASE_URL: ${{ secrets.FOIL_SMOKE_BASE_URL }}
43
+ steps:
44
+ - uses: actions/checkout@v6
45
+ - uses: actions/setup-python@v6
46
+ with:
47
+ python-version: "3.12"
48
+ - name: Require smoke secrets
49
+ if: ${{ env.FOIL_SMOKE_SECRET_KEY == '' || env.FOIL_SMOKE_ORGANIZATION_ID == '' }}
50
+ run: |
51
+ echo "FOIL_SMOKE_SECRET_KEY and FOIL_SMOKE_ORGANIZATION_ID are required for live smoke tests."
52
+ exit 1
53
+ - run: python -m pip install --upgrade pip
54
+ - run: python -m pip install -e .
55
+ - name: Run live smoke suite
56
+ run: python -m unittest tests.test_live
@@ -0,0 +1,142 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ confirm_version:
7
+ description: Version in pyproject.toml to release
8
+ required: true
9
+ type: string
10
+ dry_run:
11
+ description: Build and verify without tagging or publishing
12
+ required: true
13
+ default: true
14
+ type: boolean
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ prepare:
21
+ runs-on: ubuntu-latest
22
+ outputs:
23
+ version: ${{ steps.version.outputs.version }}
24
+ tag: ${{ steps.version.outputs.tag }}
25
+ commit_sha: ${{ steps.commit.outputs.sha }}
26
+ steps:
27
+ - uses: actions/checkout@v6
28
+ with:
29
+ fetch-depth: 0
30
+
31
+ - uses: actions/setup-python@v6
32
+ with:
33
+ python-version: "3.12"
34
+
35
+ - id: commit
36
+ run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
37
+
38
+ - id: version
39
+ run: |
40
+ VERSION=$(python - <<'PY'
41
+ import pathlib
42
+ import tomllib
43
+
44
+ with pathlib.Path("pyproject.toml").open("rb") as handle:
45
+ data = tomllib.load(handle)
46
+ print(data["project"]["version"])
47
+ PY
48
+ )
49
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
50
+ echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
51
+
52
+ - name: Validate release inputs
53
+ env:
54
+ EXPECTED_VERSION: ${{ inputs.confirm_version }}
55
+ ACTUAL_VERSION: ${{ steps.version.outputs.version }}
56
+ DRY_RUN: ${{ inputs.dry_run }}
57
+ REF_NAME: ${{ github.ref_name }}
58
+ run: |
59
+ if [ "$EXPECTED_VERSION" != "$ACTUAL_VERSION" ]; then
60
+ echo "Expected version $EXPECTED_VERSION, found $ACTUAL_VERSION."
61
+ exit 1
62
+ fi
63
+
64
+ if [ "$DRY_RUN" != "true" ] && [ "$REF_NAME" != "main" ]; then
65
+ echo "Real releases must run from main."
66
+ exit 1
67
+ fi
68
+
69
+ - name: Ensure release tag is new
70
+ env:
71
+ TAG: ${{ steps.version.outputs.tag }}
72
+ run: |
73
+ git fetch --tags --force
74
+ if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
75
+ echo "Tag $TAG already exists."
76
+ exit 1
77
+ fi
78
+
79
+ - run: python -m pip install --upgrade pip build
80
+ - run: python -m pip install -e .
81
+ - run: python -m unittest tests.test_client tests.test_contract tests.test_sealed_token
82
+ - run: python -m build
83
+
84
+ - uses: actions/upload-artifact@v7
85
+ with:
86
+ name: release-artifacts
87
+ path: dist/*
88
+ if-no-files-found: error
89
+ retention-days: 7
90
+
91
+ publish:
92
+ if: ${{ !inputs.dry_run }}
93
+ needs: prepare
94
+ runs-on: ubuntu-latest
95
+ environment: release
96
+ permissions:
97
+ contents: write
98
+ id-token: write
99
+ env:
100
+ GH_TOKEN: ${{ github.token }}
101
+ steps:
102
+ - uses: actions/checkout@v6
103
+ with:
104
+ fetch-depth: 0
105
+ ref: ${{ needs.prepare.outputs.commit_sha }}
106
+
107
+ - uses: actions/download-artifact@v8
108
+ with:
109
+ name: release-artifacts
110
+ path: dist
111
+
112
+ - name: Ensure release is still new
113
+ env:
114
+ TAG: ${{ needs.prepare.outputs.tag }}
115
+ run: |
116
+ git fetch --tags --force
117
+ if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
118
+ echo "Tag $TAG already exists."
119
+ exit 1
120
+ fi
121
+ if gh release view "$TAG" >/dev/null 2>&1; then
122
+ echo "GitHub release $TAG already exists."
123
+ exit 1
124
+ fi
125
+
126
+ - name: Create and push release tag
127
+ env:
128
+ TAG: ${{ needs.prepare.outputs.tag }}
129
+ run: |
130
+ git config user.name "github-actions[bot]"
131
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
132
+ git tag -a "$TAG" -m "Release $TAG"
133
+ git push origin "$TAG"
134
+
135
+ - uses: pypa/gh-action-pypi-publish@release/v1
136
+ with:
137
+ packages-dir: dist
138
+
139
+ - name: Create GitHub release
140
+ env:
141
+ TAG: ${{ needs.prepare.outputs.tag }}
142
+ run: gh release create "$TAG" dist/* --title "$TAG" --generate-notes --verify-tag
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ .venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ABXY Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: foil-server
3
+ Version: 0.2.3
4
+ Summary: Official Foil Python server SDK
5
+ Project-URL: Homepage, https://github.com/abxy-labs/foil-server-python
6
+ Project-URL: Repository, https://github.com/abxy-labs/foil-server-python
7
+ Project-URL: Issues, https://github.com/abxy-labs/foil-server-python/issues
8
+ Author: ABXY Labs
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: cryptography<47,>=42
13
+ Requires-Dist: httpx<1,>=0.27
14
+ Description-Content-Type: text/markdown
15
+
16
+ # Foil Python Library
17
+
18
+ ![Preview](https://img.shields.io/badge/status-preview-111827)
19
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white)
20
+ ![License: MIT](https://img.shields.io/badge/license-MIT-0f766e.svg)
21
+
22
+ The Foil Python library provides convenient access to the Foil API from applications written in Python. It includes a synchronous client for Sessions, Fingerprints, Organizations, Organization API key management, sealed token verification, Gate, and Gate delivery/webhook helpers.
23
+
24
+ The library also provides:
25
+
26
+ - a fast configuration path using `FOIL_SECRET_KEY`
27
+ - iterator helpers for cursor-based pagination
28
+ - structured API errors and built-in sealed token verification
29
+ - webhook endpoint management, test sends, and event delivery history
30
+ - public, bearer-token, and secret-key auth modes for Gate flows
31
+ - Gate delivery/webhook helpers
32
+
33
+ ## Documentation
34
+
35
+ See the [Foil docs](https://usefoil.com/docs) and [API reference](https://usefoil.com/docs/api-reference/introduction).
36
+
37
+ ## Installation
38
+
39
+ You don't need this source code unless you want to modify the package. If you just want to use the package, run:
40
+
41
+ ```bash
42
+ pip install foil-server
43
+ ```
44
+
45
+ ## Requirements
46
+
47
+ - Python 3.10+
48
+
49
+ ## Usage
50
+
51
+ Use `FOIL_SECRET_KEY` or `secret_key=...` for core detect APIs. For public or bearer-auth Gate flows, the client can also be created without a secret key:
52
+
53
+ ```python
54
+ from foil_server import Foil
55
+
56
+ client = Foil(secret_key="sk_live_...")
57
+
58
+ page = client.sessions.list(verdict="bot", limit=25)
59
+ session = client.sessions.get("sid_123")
60
+ ```
61
+
62
+ ### Sealed token verification
63
+
64
+ ```python
65
+ from foil_server import safe_verify_foil_token
66
+
67
+ result = safe_verify_foil_token(
68
+ sealed_token,
69
+ "sk_live_...",
70
+ )
71
+
72
+ if result.ok:
73
+ print(result.data.verdict, result.data.score)
74
+ else:
75
+ print(result.error)
76
+ ```
77
+
78
+ ### Pagination
79
+
80
+ ```python
81
+ for session in client.sessions.iter(search="signup"):
82
+ print(session.id, session.latest_decision.verdict)
83
+ ```
84
+
85
+ ### Fingerprints
86
+
87
+ ```python
88
+ page = client.fingerprints.list(sort="seen_count")
89
+ fingerprint = client.fingerprints.get("vid_123")
90
+ ```
91
+
92
+ ### Organizations
93
+
94
+ ```python
95
+ organization = client.organizations.get("org_123")
96
+ updated = client.organizations.update("org_123", name="New Name")
97
+ ```
98
+
99
+ ### Organization API keys
100
+
101
+ ```python
102
+ created = client.organizations.api_keys.create(
103
+ "org_123",
104
+ name="Production",
105
+ type="secret",
106
+ scopes=["sessions:list", "sessions:read"],
107
+ )
108
+
109
+ client.organizations.api_keys.revoke("org_123", created.id)
110
+ ```
111
+
112
+ ### Webhooks
113
+
114
+ ```python
115
+ endpoint = client.webhooks.create_endpoint(
116
+ "org_123",
117
+ name="Production alerts",
118
+ url="https://example.com/foil/webhook",
119
+ event_types=["session.result.persisted", "gate.session.approved"],
120
+ )
121
+
122
+ events = client.webhooks.list_events(
123
+ "org_123",
124
+ endpoint_id=endpoint.id,
125
+ type="session.result.persisted",
126
+ )
127
+
128
+ print(events.items[0].webhook_deliveries[0].status)
129
+ ```
130
+
131
+ ### Gate APIs
132
+
133
+ ```python
134
+ from foil_server import Foil, create_delivery_key_pair
135
+
136
+ client = Foil()
137
+ services = client.gate.registry.list()
138
+ session = client.gate.sessions.create(
139
+ service_id="foil",
140
+ account_name="my-project",
141
+ delivery=create_delivery_key_pair().delivery,
142
+ )
143
+
144
+ print(services[0].id, session.consent_url)
145
+ ```
146
+
147
+ ### Gate delivery and webhook helpers
148
+
149
+ ```python
150
+ from foil_server import (
151
+ create_delivery_key_pair,
152
+ create_gate_approved_webhook_response,
153
+ decrypt_gate_delivery_envelope,
154
+ parse_webhook_event,
155
+ verify_gate_webhook_signature,
156
+ )
157
+
158
+ key_pair = create_delivery_key_pair()
159
+ response = create_gate_approved_webhook_response(
160
+ {
161
+ "delivery": key_pair.delivery,
162
+ "outputs": {
163
+ "FOIL_PUBLISHABLE_KEY": "pk_live_...",
164
+ "FOIL_SECRET_KEY": "sk_live_...",
165
+ },
166
+ }
167
+ )
168
+ payload = decrypt_gate_delivery_envelope(key_pair.private_key, response.encrypted_delivery)
169
+ print(payload.outputs["FOIL_SECRET_KEY"])
170
+ raw_body = '{"id":"wevt_123","object":"webhook_event","type":"webhook.test","created":"2026-04-26T00:00:00.000Z","data":{}}'
171
+ print(
172
+ verify_gate_webhook_signature(
173
+ secret="whsec_test",
174
+ timestamp="1735776000",
175
+ raw_body=raw_body,
176
+ signature="…",
177
+ )
178
+ )
179
+ event = parse_webhook_event(raw_body)
180
+ print(event.type)
181
+ ```
182
+
183
+ ### Error handling
184
+
185
+ ```python
186
+ from foil_server import FoilApiError
187
+
188
+ try:
189
+ client.sessions.list(limit=999)
190
+ except FoilApiError as error:
191
+ print(error.status, error.code, error.message)
192
+ ```
193
+
194
+ ## Support
195
+
196
+ If you need help integrating Foil, start with [usefoil.com/docs](https://usefoil.com/docs).
@@ -0,0 +1,181 @@
1
+ # Foil Python Library
2
+
3
+ ![Preview](https://img.shields.io/badge/status-preview-111827)
4
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white)
5
+ ![License: MIT](https://img.shields.io/badge/license-MIT-0f766e.svg)
6
+
7
+ The Foil Python library provides convenient access to the Foil API from applications written in Python. It includes a synchronous client for Sessions, Fingerprints, Organizations, Organization API key management, sealed token verification, Gate, and Gate delivery/webhook helpers.
8
+
9
+ The library also provides:
10
+
11
+ - a fast configuration path using `FOIL_SECRET_KEY`
12
+ - iterator helpers for cursor-based pagination
13
+ - structured API errors and built-in sealed token verification
14
+ - webhook endpoint management, test sends, and event delivery history
15
+ - public, bearer-token, and secret-key auth modes for Gate flows
16
+ - Gate delivery/webhook helpers
17
+
18
+ ## Documentation
19
+
20
+ See the [Foil docs](https://usefoil.com/docs) and [API reference](https://usefoil.com/docs/api-reference/introduction).
21
+
22
+ ## Installation
23
+
24
+ You don't need this source code unless you want to modify the package. If you just want to use the package, run:
25
+
26
+ ```bash
27
+ pip install foil-server
28
+ ```
29
+
30
+ ## Requirements
31
+
32
+ - Python 3.10+
33
+
34
+ ## Usage
35
+
36
+ Use `FOIL_SECRET_KEY` or `secret_key=...` for core detect APIs. For public or bearer-auth Gate flows, the client can also be created without a secret key:
37
+
38
+ ```python
39
+ from foil_server import Foil
40
+
41
+ client = Foil(secret_key="sk_live_...")
42
+
43
+ page = client.sessions.list(verdict="bot", limit=25)
44
+ session = client.sessions.get("sid_123")
45
+ ```
46
+
47
+ ### Sealed token verification
48
+
49
+ ```python
50
+ from foil_server import safe_verify_foil_token
51
+
52
+ result = safe_verify_foil_token(
53
+ sealed_token,
54
+ "sk_live_...",
55
+ )
56
+
57
+ if result.ok:
58
+ print(result.data.verdict, result.data.score)
59
+ else:
60
+ print(result.error)
61
+ ```
62
+
63
+ ### Pagination
64
+
65
+ ```python
66
+ for session in client.sessions.iter(search="signup"):
67
+ print(session.id, session.latest_decision.verdict)
68
+ ```
69
+
70
+ ### Fingerprints
71
+
72
+ ```python
73
+ page = client.fingerprints.list(sort="seen_count")
74
+ fingerprint = client.fingerprints.get("vid_123")
75
+ ```
76
+
77
+ ### Organizations
78
+
79
+ ```python
80
+ organization = client.organizations.get("org_123")
81
+ updated = client.organizations.update("org_123", name="New Name")
82
+ ```
83
+
84
+ ### Organization API keys
85
+
86
+ ```python
87
+ created = client.organizations.api_keys.create(
88
+ "org_123",
89
+ name="Production",
90
+ type="secret",
91
+ scopes=["sessions:list", "sessions:read"],
92
+ )
93
+
94
+ client.organizations.api_keys.revoke("org_123", created.id)
95
+ ```
96
+
97
+ ### Webhooks
98
+
99
+ ```python
100
+ endpoint = client.webhooks.create_endpoint(
101
+ "org_123",
102
+ name="Production alerts",
103
+ url="https://example.com/foil/webhook",
104
+ event_types=["session.result.persisted", "gate.session.approved"],
105
+ )
106
+
107
+ events = client.webhooks.list_events(
108
+ "org_123",
109
+ endpoint_id=endpoint.id,
110
+ type="session.result.persisted",
111
+ )
112
+
113
+ print(events.items[0].webhook_deliveries[0].status)
114
+ ```
115
+
116
+ ### Gate APIs
117
+
118
+ ```python
119
+ from foil_server import Foil, create_delivery_key_pair
120
+
121
+ client = Foil()
122
+ services = client.gate.registry.list()
123
+ session = client.gate.sessions.create(
124
+ service_id="foil",
125
+ account_name="my-project",
126
+ delivery=create_delivery_key_pair().delivery,
127
+ )
128
+
129
+ print(services[0].id, session.consent_url)
130
+ ```
131
+
132
+ ### Gate delivery and webhook helpers
133
+
134
+ ```python
135
+ from foil_server import (
136
+ create_delivery_key_pair,
137
+ create_gate_approved_webhook_response,
138
+ decrypt_gate_delivery_envelope,
139
+ parse_webhook_event,
140
+ verify_gate_webhook_signature,
141
+ )
142
+
143
+ key_pair = create_delivery_key_pair()
144
+ response = create_gate_approved_webhook_response(
145
+ {
146
+ "delivery": key_pair.delivery,
147
+ "outputs": {
148
+ "FOIL_PUBLISHABLE_KEY": "pk_live_...",
149
+ "FOIL_SECRET_KEY": "sk_live_...",
150
+ },
151
+ }
152
+ )
153
+ payload = decrypt_gate_delivery_envelope(key_pair.private_key, response.encrypted_delivery)
154
+ print(payload.outputs["FOIL_SECRET_KEY"])
155
+ raw_body = '{"id":"wevt_123","object":"webhook_event","type":"webhook.test","created":"2026-04-26T00:00:00.000Z","data":{}}'
156
+ print(
157
+ verify_gate_webhook_signature(
158
+ secret="whsec_test",
159
+ timestamp="1735776000",
160
+ raw_body=raw_body,
161
+ signature="…",
162
+ )
163
+ )
164
+ event = parse_webhook_event(raw_body)
165
+ print(event.type)
166
+ ```
167
+
168
+ ### Error handling
169
+
170
+ ```python
171
+ from foil_server import FoilApiError
172
+
173
+ try:
174
+ client.sessions.list(limit=999)
175
+ except FoilApiError as error:
176
+ print(error.status, error.code, error.message)
177
+ ```
178
+
179
+ ## Support
180
+
181
+ If you need help integrating Foil, start with [usefoil.com/docs](https://usefoil.com/docs).
@@ -0,0 +1,35 @@
1
+ # Releasing `foil-server`
2
+
3
+ This repository uses a manual `release.yml` workflow. Versions are independent from the other Foil server SDKs.
4
+
5
+ ## Before the first real release
6
+
7
+ Configure these GitHub and registry settings:
8
+
9
+ 1. Create a protected GitHub environment named `release` with required reviewer approval.
10
+ 2. Create the `foil-server` project on PyPI if it does not already exist.
11
+ 3. Add a pending PyPI trusted publisher for:
12
+ - repository: `abxy-labs/foil-server-python`
13
+ - workflow: `.github/workflows/release.yml`
14
+ - environment: `release`
15
+
16
+ ## Release flow
17
+
18
+ 1. Bump `pyproject.toml` to the target version in a pull request.
19
+ 2. Merge the version bump to `main`.
20
+ 3. Open GitHub Actions and run `Release`.
21
+ 4. Set `confirm_version` to the exact `pyproject.toml` version.
22
+ 5. Run a `dry_run=true` release first.
23
+ 6. Re-run with `dry_run=false` once the dry run looks correct and the `release` environment approval is in place.
24
+
25
+ ## What the workflow does
26
+
27
+ - reads the release version from `pyproject.toml`
28
+ - reruns the equivalent of the repo checks
29
+ - builds the wheel and source distribution with `python -m build`
30
+ - on real releases:
31
+ - creates tag `vX.Y.Z`
32
+ - publishes `foil-server` to PyPI with trusted publishing
33
+ - creates a GitHub Release with the built artifacts attached
34
+
35
+ GitHub Release notes use autogenerated notes. There is no prerelease channel in this phase.