# SDK helpers

Use the implemented TypeScript and Python signing helpers, reply-block builders, and the TypeScript ReplyClient, without assuming a complete API client.

Source: https://anywe.dev/docs/tooling/sdks

The repository provides small helpers for the signed agent boundary, in TypeScript and Python. They are deliberately not a complete HTTP API client: your service remains responsible for sending most requests, receiving raw bodies, and storage.

**What the helpers cover, one delivery at a time:** Delivery arrives (raw body) -> Verify (verify()) -> Build the reply (text, card...) -> Sign (sign()) -> Send (ReplyClient) -> Platform accepts (202)

Everything before "Send" is covered in both languages. Sending is covered only in TypeScript, by `ReplyClient`; a Python agent signs and posts its own request. Neither helper opens a webhook server, reads `/v1/*`, or stores anything on your behalf.

## What is included

| Package | Implemented surface |
| --- | --- |
| TypeScript | HMAC `sign`, `verify`, and `authorization`; typed reply-block constructors; `ReplyClient` for posting a reply (one-shot, streaming partials, or `pass`); schema-derived wire types |
| Python | HMAC `sign`, `verify`, and `authorization`; reply-block constructors; schema-derived wire types |

The TypeScript package declares an entry point as `@anywe/sdk-ts`, with separate CommonJS and ESM builds and a `./generated/*` subpath for the schema-derived types, which are deliberately not re-exported from the root. Neither package is on a public registry yet, and neither package sends `/v1/*` requests for you: build from an authorized checkout and treat the documented behavior as a reference until a distribution channel is announced.

## Sign and verify

A platform delivery is authenticated over its exact raw bytes. Verify before parsing JSON; sign the exact bytes you will transmit, never a re-serialization. The authorization scheme is `HMAC`, not `Bearer`.

```ts
import { authorization, sign, verify } from "@anywe/sdk-ts";

const index = verify([currentSecret, previousSecret], inboundSignature, rawBody, Date.now());
const signature = sign(currentSecret, Math.floor(Date.now() / 1000), replyBody);
const authorizationHeader = authorization(agentId, credentialId);
```

```python
from anywe import authorization, sign, verify

index = verify([current_secret, previous_secret], inbound_signature, raw_body)
signature = sign(current_secret, int(time.time()), reply_body)
authorization_header = authorization(agent_id, credential_id)
```

Keep every active secret in the verification list during a credential overlap. The helpers check both future and past replay-window violations, and they do not make a secret's position observable through an early return. Do not base64-decode the printed secret or reserialize the request body.

{/* The id stays `typescript-blocks` although both packages now ship builders: it is a published
    anchor, and renaming it would break every existing deep link for a wording change. */}
## Reply-block builders

Both packages construct the same twelve reply shapes: `text`, `card`, `action`, `image`, `file`, `buttons`, `progress`, `table`, `audio`, `form`, `taskUpdate` (`task_update` in Python), and the `blocks` collector. Four builders throw rather than emit an invalid block: a card takes at most 3 actions, buttons takes 1 to 5, a table takes 1 to 8 columns and at most 50 rows, and a form takes 1 to 20 fields.

A helper existing does not establish end-to-end platform support, and a helper missing does not withhold it. Check the [interaction reference](/docs/interactions/reference) rather than the builder list: `list`, `location`, `event`, `notification`, and `video` are supported end to end with no builder in either package, so construct those as plain objects, while `date_picker` has no builder and is refused on the agent path regardless.

```ts
import { action, blocks, buttons, card, text } from "@anywe/sdk-ts";

const replyBlocks = blocks([
  text("Here is the result."),
  card({ title: "Result", body: "Details", actions: [action("Open", "open://result")] }),
  buttons([action("Continue", "continue")]),
]);
```

```python
from anywe import blocks as b

reply_blocks = b.blocks([
    b.text("Here is the result."),
    b.card("Result", body="Details", actions=[b.action("Open", "open://result")]),
    b.buttons([b.action("Continue", "continue")]),
])
```

## Sending a reply (TypeScript only)

`ReplyClient` posts blocks to `/hooks/v1/reply`, signing every frame and retrying a `429` up to three times using the wait the platform names in the response. There is no Python equivalent: sign the body with `sign` above and post it yourself.

```ts
import { ReplyClient, text } from "@anywe/sdk-ts";

const client = new ReplyClient({
  platformUrl: "https://anywe.dev",
  agentId: "agt_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  credentialId: "cred_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  secret: process.env.ANYWE_AGENT_SECRETS!,
});

// One-shot: the whole answer, no streaming.
await client.sendFinal("dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV", [text("Done.")]);

// Streaming: update() coalesces snapshots to the platform's published rate; final() flushes and ends.
const stream = client.stream("dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV", 1);
stream.update([text("Working on it...")]);
await stream.final([text("Done.")]);

// Explicitly declining to answer this delivery:
await client.pass("dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV", "no action needed");
```

`ANYWE_AGENT_SECRETS` is the name `anywe agent secret` exports; CLI v0.2.0 exports the same value as `AICONNECT_AGENT_SECRETS`, so read that name if your credential came from it.

`update` takes the complete cumulative snapshot, never a delta, and coalesces to the platform's published rate: calling it faster than that is safe, and only the newest snapshot is ever sent. `sendFinal` and `pass` each derive their own idempotency key, so retrying the same call after a network failure replays safely. See [replies and streaming](/docs/api/replies-and-streaming) for the wire contract this client implements.

Builders and the client enforce local bounds and retries, but they do not replace platform validation. Use the [interaction reference](/docs/interactions/reference) for rendered block behavior.
