# AEGVEN language SDKs

AEGVEN provides TypeScript/JavaScript, C#/.NET, Python and Go clients for the same versioned public API. All clients register a `FinancialObligation`, obtain its secure hosted interaction, and operate on the same accepted records as the Web Console. The invoice source adapter is the currently enabled obligation type. Phase 0 moves no money.

## Packages and installation

Version 0.1.0 is distributed as controlled customer development artifacts. Obtain the approved artifacts, SHA256SUMS, API base URL and organization access from your AEGVEN onboarding contact. Public npm, NuGet, PyPI and Go module publication has not been performed; the commands below install the supplied files.

| Language                | Runtime      | Artifact / identity                                  |
| ----------------------- | ------------ | ---------------------------------------------------- |
| TypeScript / JavaScript | Node.js 22+  | `aegven-sdk-0.1.0.tgz`, `@aegven/sdk`                |
| C# / .NET               | .NET 8+      | `Aegven.Sdk.0.1.0.nupkg`, namespace `Aegven`         |
| Python                  | Python 3.11+ | `aegven_sdk-0.1.0-py3-none-any.whl`, import `aegven` |
| Go                      | Go 1.22+     | `aegven-go-0.1.0.tar.gz`, module `aegven.com/sdk/go` |

The three additional SDKs use only their language standard libraries at runtime. Python includes generated `TypedDict`/`Literal` types and a `py.typed` marker. Go includes generated structs and distinct action payloads. C# includes generated records with JSON field mappings, required properties and distinct action records. Money is always an exact minor-unit **string**, including in responses. SDKs perform transport validation only; financial decisions remain in AEGVEN.

```sh
npm install ./aegven-sdk-0.1.0.tgz

# From your .NET application, point --source at the supplied artifact directory.
dotnet add package Aegven.Sdk --version 0.1.0 --source /path/to/artifacts

python3 -m venv .venv
.venv/bin/python -m pip install ./aegven_sdk-0.1.0-py3-none-any.whl

# Controlled Go source distribution; keep this vendored directory with your application.
mkdir -p vendor-src
tar -xzf aegven-go-0.1.0.tar.gz -C vendor-src
go mod edit -require=aegven.com/sdk/go@v0.1.0
go mod edit -replace=aegven.com/sdk/go=./vendor-src/aegven-go-0.1.0
go mod tidy
```

The Go module identity is reserved for this package; it currently requires the local replacement above. No public module discovery endpoint is advertised. Verify supplied artifacts against SHA256SUMS before installing. Pin the approved SDK version; review release notes and the supplied OpenAPI contract before upgrading.

## Credentials and source registration

An organization administrator or developer signs into the Web Console with an individual account and creates a machine credential under Developers → API keys. Store its one-time secret in the integration server's secret manager. Configure `AEGVEN_API_URL` (including `/api/v1`) and `AEGVEN_API_KEY` on that server. The new SDKs require HTTPS except for explicit localhost, `127.0.0.1`, or `::1` development URLs. They do not follow redirects or use human login cookies.

Set `SOURCE_RECORD_ID` to your source system's stable identifier and `AEGVEN_IDEMPOTENCY_KEY` to a durably stored registration key. The following examples deliberately do not generate a fresh retry key. Persist the returned obligation ID and hosted URL on the original record; your existing communication process inserts the URL into its outgoing message. Treat that URL as a secret bearer capability. Do not put machine credentials or SDK clients in recipient browsers. Recipients continue using scoped secure links without accounts.

### Python

```python
import os
from aegven import Aegven, models

client = Aegven(os.environ['AEGVEN_API_URL'], os.environ['AEGVEN_API_KEY'])
request: models.RegisterObligation = {
    'obligation_type': 'invoice',
    'external_reference': {'namespace': 'company-source', 'id': os.environ['SOURCE_RECORD_ID']},
    'debtor': {'name': 'Recipient company', 'email': 'accounts@example.invalid'},
    'amount_minor': '125000',
    'currency': 'CAD',
    'deadline': '2027-01-05',
    'business_context': {'invoice_number': 'REF-1042'},
}
created = client.register_obligation(request, os.environ['AEGVEN_IDEMPOTENCY_KEY'])
obligation = created.get('obligation')
obligation_id = obligation['id'] if obligation else created['invoice']['id']
payment_request = created.get('payment_request') or client.get_payment_request(obligation_id)
# Persist obligation_id and payment_request['hosted_url'] in your source record.
```

Python is synchronous. Its configurable `timeout` defaults to 15 seconds per blocking socket operation; there is no async client or in-flight cancellation API in this version. Use your worker's execution deadline when scheduling calls. The generated annotations aid static checking; they are not runtime schema validation.

### C# / .NET

```csharp
using Aegven;

using var client = new AegvenClient(
    Environment.GetEnvironmentVariable("AEGVEN_API_URL")!,
    Environment.GetEnvironmentVariable("AEGVEN_API_KEY")!);
var request = new RegisterObligation
{
    ObligationType = "invoice",
    ExternalReference = new() { Namespace = "company-source", Id = Environment.GetEnvironmentVariable("SOURCE_RECORD_ID")! },
    Debtor = new() { Name = "Recipient company", Email = "accounts@example.invalid" },
    AmountMinor = "125000", Currency = "CAD", Deadline = "2027-01-05",
    BusinessContext = new() { InvoiceNumber = "REF-1042" },
};
var created = await client.RegisterObligationAsync(request,
    Environment.GetEnvironmentVariable("AEGVEN_IDEMPOTENCY_KEY")!);
var obligationId = created.Obligation?.Id ?? created.Invoice.Id;
var paymentRequest = created.PaymentRequest ?? await client.GetPaymentRequestAsync(obligationId);
// Persist obligationId and paymentRequest.HostedUrl in your source record.
```

Reuse one `AegvenClient` for the integration service's lifetime and dispose it at shutdown. All operations accept an optional `CancellationToken`; the default HTTP timeout is 15 seconds per attempt. The constructor accepts a custom timeout. Canceling a call does not prove the server rolled back a mutation.

### Go

```go
package main

import (
    "context"
    "os"
    "time"

    aegven "aegven.com/sdk/go"
)

func main() {
    client, err := aegven.New(aegven.Options{
        BaseURL: os.Getenv("AEGVEN_API_URL"), APIKey: os.Getenv("AEGVEN_API_KEY"),
    })
    if err != nil { panic(err) }
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    created, err := client.RegisterObligation(ctx, aegven.RegisterObligation{
        ObligationType: "invoice",
        ExternalReference: aegven.ExternalReference{Namespace: "company-source", Id: os.Getenv("SOURCE_RECORD_ID")},
        Debtor: aegven.RegisterObligationDebtor{Name: "Recipient company", Email: "accounts@example.invalid"},
        AmountMinor: "125000", Currency: "CAD", Deadline: "2027-01-05",
        BusinessContext: aegven.BusinessContext{InvoiceNumber: "REF-1042"},
    }, os.Getenv("AEGVEN_IDEMPOTENCY_KEY"))
    if err != nil { panic(err) }
    var obligationID string
    if created.Obligation != nil {
        obligationID = created.Obligation.Id
    } else {
        obligationID = created.Invoice.Id // Compatibility with older accepted replays.
    }
    paymentRequest, err := client.GetPaymentRequest(ctx, obligationID)
    if err != nil { panic(err) }
    _ = paymentRequest // Persist HostedUrl together with obligationID.
}
```

Go clients can be shared across goroutines. Every call requires a context. The HTTP client defaults to a 15-second per-attempt timeout. A supplied `Options.HTTPClient` is copied and retains a positive timeout; cookies and redirects are disabled. Custom transports must preserve TLS verification and protect credentials. Neither cancellation nor deadline expiry proves a mutation was rejected.

## Supported workflows

All 33 machine-authorized OpenAPI operations have named methods in each new SDK. Python uses snake_case, Go uses PascalCase, and C# uses PascalCase with `Async`.

| Capability                      | Python                                                                                                                                | Go                                                       | C#                                                                      |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------- |
| Register / read / list          | `register_obligation`, `get_obligation`, `list_obligations`                                                                           | `RegisterObligation`, `GetObligation`, `ListObligations` | `RegisterObligationAsync`, `GetObligationAsync`, `ListObligationsAsync` |
| Hosted interaction              | `get_payment_request`                                                                                                                 | `GetPaymentRequest`                                      | `GetPaymentRequestAsync`                                                |
| Apply versioned command         | `apply_action`                                                                                                                        | `ApplyAction`                                            | `ApplyActionAsync`                                                      |
| Documents                       | `upload_document`, `download_document`                                                                                                | `UploadDocument`, `DownloadDocument`                     | `UploadDocumentAsync`, `DownloadDocumentAsync`                          |
| Investigate uncertain results   | `lookup_operation`, `get_operation`                                                                                                   | `LookupOperation`, `GetOperation`                        | `LookupOperationAsync`, `GetOperationAsync`                             |
| Webhook configuration           | `register_webhook`, `list_webhook_endpoints`, `update_webhook_endpoint`, `rotate_webhook_secret`                                      | Corresponding PascalCase methods                         | Corresponding PascalCase methods + `Async`                              |
| Delivery test / history / retry | `test_webhook_endpoint`, `list_webhook_deliveries`, `retry_webhook_delivery`                                                          | Corresponding PascalCase methods                         | Corresponding PascalCase methods + `Async`                              |
| Setup and operations            | `get_readiness`, `get_overview`, `list_interactions`, `list_observations`, `list_exceptions`, `list_reconciliation`, `list_documents` | Corresponding PascalCase methods                         | Corresponding PascalCase methods + `Async`                              |
| CSV bridge                      | `preview_import`, `accept_import`, `export_accepted_results`                                                                          | Corresponding PascalCase methods                         | Corresponding PascalCase methods + `Async`                              |

Legacy invoice routes are retained for compatibility (`create_invoice`, `get_invoice`, `list_invoices`, `prepare_delivery`, `apply_invoice_action`, `upload_invoice_document`, `download_invoice_document`, with the same naming conventions in Go/C#). New integrations should start with canonical obligation methods. Registration replays accepted before canonical response additions may only contain `invoice`; its ID is the same obligation ID.

Mutation methods require an explicit 8–200 character printable idempotency key. Lists expose the API's existing pagination: obligations/invoices accept limit and offset; activity accepts offset. Use `ListObligations(ctx, 50, 0)` in Go. There is no hidden auto-pagination or financial status calculation. The API remains authoritative for supported actions, permissions and validation errors. Human sessions, member management and machine-key creation/rotation/revocation are intentionally not exposed through an API-key client. Use individual console access for those operations.

Documents take the original bytes, plain filename, media type, document kind and expected version. Uploads are buffered so retries use identical bytes and the same multipart boundary. Downloads and exports return bytes. Preserve the existing server limits and document authorization; this SDK does not introduce scanning, storage or public download access.

## Safe retries and errors

Keep the request body and idempotency key durably together. Each new client makes at most two application-level attempts on transport/read failures or HTTP 502/503/504, reusing the exact encoded body and key. Other errors are returned immediately. No automatic business retry is performed for 409 or 422. A Go custom transport or the standard transport's connection recovery may have its own safe connection retry behavior.

Errors expose `code`, `status`, `idempotency_key` in Python; `Code`, `Status`, `IdempotencyKey` in Go/.NET. The exception types are `AegvenError`, `*aegven.AegvenError` and `AegvenException`. Their diagnostic messages exclude raw server/transport messages, request bodies and credentials. Preserve the original key externally even when your context/cancellation token fires. Do not enable HTTP-body logging on authenticated calls.

- `ambiguous_network_result`: the call may have committed. Query `lookup_operation` with the original key or replay the same request and key. Empty lookup results during in-flight processing do not establish that a call was rejected.
- `idempotency_conflict`: this key was used with different input. Recover the original request; do not silently change its meaning.
- `version_conflict`: fetch the current obligation and review the accepted history before issuing a new decision with a new key and current `expected_version`.
- `duplicate_reference`: investigate the namespaced source mapping rather than creating a second source identity.
- `invalid_response`: investigate the operation before retrying; a malformed reply does not establish rejection.
- HTTP 401/403: check machine credentials and authorization. HTTP 410 for a hosted request means expired/revoked access; registration replay does not renew the link.

Signing-secret rotation responses disclose a new secret once. A replay may contain metadata only. If the first response is lost, investigate the operation and deliberately issue another rotation with a new key to obtain an accessible secret. Keep that distinction from financial-operation replay recovery.

## Webhooks and status meaning

Register an endpoint with `RegisterWebhookRequest` (`url` and a server-held signing secret of 32–256 characters). Run the test method and inspect deliveries plus readiness. Both tests and financial events use the existing signed, retrying transactional outbox. No SDK delivers or bypasses the outbox itself.

Read the exact HTTP body bytes and the **`Aegven-Signature`** header before parsing JSON. Verification helpers check HMAC-SHA256 in constant time and enforce a default/recommended five-minute past/future timestamp window:

```python
from aegven import verify_webhook
valid = verify_webhook(raw_body, signature_header, signing_secret)
```

```csharp
var valid = Webhooks.Verify(rawBody, signatureHeader, signingSecret);
```

```go
valid := aegven.VerifyWebhook(rawBody, signatureHeader, signingSecret, time.Now(), 5*time.Minute)
```

Reject invalid signatures. After verification, durably deduplicate event IDs before applying source-system effects, then acknowledge promptly. Handle retries and out-of-order deliveries using canonical versions; fetch current state when necessary. Do not recreate signatures from parsed/re-serialized JSON. Test events use the generated `IntegrationTestEvent` shape (`type: integration.test`, `resource_type: webhook_endpoint`) and contain no obligation. Financial events use `ObligationEvent`.

A payer report is an unverified `PaymentClaim`; an external record is a `PaymentObservation`. Only accepted evidence and allocations affect reconciliation. Display the returned `OBLIGATION_CAPTURED`, `PAYMENT_REQUESTED`, `PAYMENT_CLAIMED`, `PARTIALLY_RECONCILED` and `RECONCILED` states faithfully. No SDK interprets “I paid” as settlement or calculates authoritative allocations.

## Maintainer checks and controlled release

From the application repository:

```sh
python3 sdk/generator/generate.py
make sdk-check
make sdk-package-all
make sdk-integration # Requires the running development stack; creates synthetic organizations.
```

`api/openapi.json` is authoritative. The bounded generator emits models and machine operations for all three languages, fails on unreviewed structural schemas/routes, and supports `--check` to detect drift. Generate TypeScript with `npm run generate:api` after contract changes. No Rust or database changes are required for these SDKs.

`make sdk-check` runs shared HTTP fault/signature fixtures, Go race tests, C# compilation/executable tests and Python type checks. `make sdk-package-all` produces versioned artifacts and checksums under `.local/sdk-artifacts`, installs packages in independent consumer directories and compiles their imports. `make sdk-integration` uses operator-provisioned isolated organizations, then only the public API to test registration, semantic replay, isolation, evidence, partial/full reconciliation, CSV and webhook setup. The original complete recipient lifecycle remains covered by `make integration` and browser tests.

The release workflow does not publish packages. Public registry feeds, signed release provenance, expanded runtime/OS matrices and full backwards compatibility baselines remain future work. A controlled artifact release also does not deploy a customer environment or establish readiness for live financial data.
