# Build Channels with the SDK

> Author Channel protocols and Auth Providers that normalize communication without claiming platform authority.

Use `authProvider()` to verify ingress and `channel()` to translate a protocol into canonical events.

## Verify the caller {#verify-caller}

```ts
import { authProvider } from "@constal/sdk";

export default authProvider({
  id: "signed-webhook", version: "1.0.0",
  needs: [{ binding: "verifier", kind: "service", ops: ["verify"] }],
  async authenticate({ request }, context) {
    const proof = await context.invoke<{ valid: boolean; subject?: string }>(
      context.resources.verifier!, "verify", { headers: request.headers, bodyBase64: request.bodyBase64 },
    );
    return proof.valid && proof.subject
      ? { authenticated: true, subject: proof.subject }
      : { authenticated: false, reason: "invalid signature" };
  },
});
```

## Normalize the protocol {#normalize-protocol}

```ts
import { channel } from "@constal/sdk";

export default channel({
  id: "webhook", version: "1.0.0", public: true,
  authProvider: { kind: "local", resourceKind: "auth-provider", id: "signed-webhook" },
  protocol: {
    id: "json-webhook", version: "1",
    receive(request) {
      const body = JSON.parse(atob(request.bodyBase64 ?? ""));
      return { id: body.id, type: "message", session: body.session, data: body.message };
    },
    respond(result) {
      return { status: result.status === "failed" ? 500 : 200,
        headers: { "content-type": "application/json" }, bodyBase64: btoa(JSON.stringify(result)) };
    },
  },
});
```

Auth Provider output is evidence, not platform authority. Central authentication assigns tenant and customer identity before `receive`. Both contexts expose declared Resources through governed `invoke()` and never expose Credential bytes. See [Deploy a Channel](/docs/channels/deploy.md) and [Deploy an Auth Provider](/docs/channels/auth-providers/deploy.md).

The local Auth Provider reference is package identity, not runtime authority. Direct deployment and catalog installation resolve it inside the authenticated tenant and namespace, verify its kind, and record the exact selected Auth Provider on the Channel revision.

Routing is not embedded in Channel code. `constal.channel.json` owns an Agent `target` Resource selector; Agent manifests own labels. If one Agent matches, `receive` may omit its event `target`. If several match, the protocol may return a target Agent CRN, but the platform rejects it unless that Agent matches the pinned selector.

Keep event and delivery ids stable so retries resolve to recorded outcomes. An optional `send` handler uses the same declared Channel context for outbound delivery. Register analytics definitions on the Channel before emitting them.
