> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praxa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a governed mission lifecycle

> Use @praxa/sdk to submit intent, create a budgeted mission, stream events, send signals, cancel safely, and test a deployment-specific Integration Gateway.

This tutorial targets the versioned Integration Gateway in `@praxa/sdk@0.3.0`.
It does not target `api.praxa.io/v1`. You need the HTTPS origin and short-lived
OAuth issuance flow from your Praxa deployment operator.

## Prerequisites

```bash theme={null}
npm install @praxa/sdk@0.3.0
export PRAXA_BASE_URL="https://your-gateway.example"
export PRAXA_ACCESS_TOKEN="<short-lived-delegated-token>"
```

Request only the scopes you use. Creating, signaling, or cancelling a mission
requires `missions:write`; reading or streaming it requires `missions:read`.

## 1. Create the client

```ts theme={null}
import { PraxaClient } from "@praxa/sdk";

const client = new PraxaClient({
  baseUrl: process.env.PRAXA_BASE_URL!,
  accessToken: async () => refreshPraxaTokenBeforeExpiry(),
  maximumAttempts: 3,
  retryBaseDelayMs: 100,
});
```

Use a token function in production so each request can obtain a current token.
The SDK retries safe reads and idempotency-keyed mutations only.

## 2. Choose intent or mission intake

<Tabs>
  <Tab title="Natural-language intent">
    Use `submitIntent` when the deployment owns deterministic compilation.

    ```ts theme={null}
    const idempotencyKey = "weekly-review-2026-08-13";
    const submission = await client.submitIntent(
      "Prepare the weekly review",
      idempotencyKey,
    );

    console.log(submission.submissionId, submission.disposition);
    ```

    Acceptance records intent. It does not start or prove a provider action.
  </Tab>

  <Tab title="Canonical goal spec">
    Use `createMission` when your application already owns the accepted goal
    specification and resource budget.

    ```ts theme={null}
    const mission = await client.createMission(
      {
        goalSpec: { task: "Prepare the weekly review" },
        resourceBudget: {
          maximumSteps: 12,
          maximumToolCalls: 8,
          maximumElapsedMs: 120_000,
          maximumParallelism: 2,
        },
      },
      "weekly-review-2026-08-13",
    );

    console.log(mission.runId, mission.status);
    ```
  </Tab>
</Tabs>

Persist the idempotency key before the first request. Reuse it for retries of
that exact logical mutation; generate a new key for different work.

## 3. Consume resumable events

```ts theme={null}
let lastEventId: string | undefined;

for await (const event of client.missionEvents(mission.runId, { lastEventId })) {
  lastEventId = event.id;
  await saveCursor(mission.runId, event.id);
  await projectEvent(event);
}
```

On reconnect, load the saved cursor and pass it as `lastEventId`. Do not assume
that receiving one event proves the mission completed.

## 4. Signal or cancel deliberately

```ts theme={null}
await client.signalMission(
  mission.runId,
  "review_ready",
  { reviewer: "operations" },
  "weekly-review-signal-2026-08-13",
);

await client.cancelMission(
  mission.runId,
  "Operator requested cancellation",
  "weekly-review-cancel-2026-08-13",
);
```

Cancellation is a durable request, not proof that an already committed effect
was rolled back.

## 5. Use the client from a framework

<Tabs>
  <Tab title="Next.js server action">
    ```ts theme={null}
    "use server";

    export async function createReviewMission(requestId: string) {
      const session = await requireSession();
      const idempotencyKey = await idempotencyKeyFor(session.user.id, requestId);
      return client.createMission(
        {
          goalSpec: { task: "Prepare the weekly review" },
          resourceBudget: {
            maximumSteps: 12,
            maximumToolCalls: 8,
            maximumElapsedMs: 120_000,
            maximumParallelism: 2,
          },
        },
        idempotencyKey,
      );
    }
    ```
  </Tab>

  <Tab title="Express">
    ```ts theme={null}
    app.post("/missions", async (request, response, next) => {
      try {
        const idempotencyKey = request.get("Idempotency-Key");
        if (!idempotencyKey) {
          return response.status(400).json({ error: "Idempotency-Key is required" });
        }
        const missionInput = validateMissionInput(request.body);
        const mission = await client.createMission(
          missionInput,
          idempotencyKey,
        );
        response.status(202).json(mission);
      } catch (error) {
        next(error);
      }
    });
    ```
  </Tab>
</Tabs>

Keep OAuth refresh and `PraxaClient` construction in trusted server code.
Persist the application request ID before the first attempt; do not generate a
new value inside a retrying server action. `validateMissionInput` and
`idempotencyKeyFor` are application-owned fail-closed validation helpers.

## 6. Verify end to end

1. Require the exact Gateway origin, contract version, and token audience for your deployment.
2. Create a token with `missions:write` and a second token with `missions:read` only.
3. Create a mission and require a returned `runId`.
4. Replay the exact keyed request and require the same durable mission.
5. Stream at least one event, disconnect, reconnect with `Last-Event-ID`, and require no duplicate sequence.
6. Read the mission with the read token and require the same run.
7. Attempt a mutation with the read-only token and require `403`.
8. Revoke a disposable token and require subsequent requests to fail closed.

<Warning>
  The public npm package proves client behavior. A successful end-to-end test
  additionally requires your deployment's OAuth authority, Gateway, private
  policy plane, and runtime to be configured and reachable.
</Warning>

## Troubleshooting

| Symptom                             | Resolution                                                                         |
| ----------------------------------- | ---------------------------------------------------------------------------------- |
| Admission is reported as completion | Follow the run, stream, delivery, or receipt to authoritative completion evidence. |
| Reconnect duplicates events         | Commit the cursor only after durable processing and deduplicate by event identity. |
| Retry creates duplicate work        | Reuse the original idempotency key and exact request body.                         |
| Failure is ambiguous                | Record unknown state and reconcile instead of starting new work.                   |

## Best practices

* Persist identity before I/O and state transitions after durable processing.
* Treat admission, delivery attempt, and cancellation request as non-terminal acknowledgements.
* Verify signatures against the raw body before parsing webhook JSON.
* Deduplicate streams and webhooks using stable event or delivery identity.
* Test disconnect, duplicate, out-of-order, timeout, revocation, and cleanup paths.

## Optimize for production

* Prefer event-driven updates while retaining bounded polling or readback reconciliation.
* Commit cursors in batches only when that cannot lose acknowledged application work.
* Keep webhook handlers short: verify, persist, acknowledge, then process asynchronously.
* Measure admission-to-terminal time, reconnect rate, duplicate rate, delivery latency, and reconciliation backlog.

Optimize only after the correctness and isolation matrix passes. Lower latency or cost is not an improvement if verified outcomes, authority checks, or recovery rates regress.

## Cleanup and next steps

1. Cancel or terminally reconcile disposable runs.
2. Disable test webhook endpoints and remove their signing secrets.
3. Delete synthetic inbox, cursor, and delivery records after assertions.
4. Revoke disposable credentials and keep only redacted lifecycle evidence.

After cleanup, run the [shared integration test matrix](/tutorials/test-your-integration) and record any environment-specific check that remains pending.

## Frequently asked questions

### What proves this tutorial works?

The minimum observable result is that a mission can be created, streamed, read, signaled or cancelled, and reconciled safely. A compile, package import, mocked response, or initial admission alone does not prove the complete workflow.

### Can a browser, mobile app, or model prompt hold the credential?

No. The trusted runtime owns Praxa keys, OAuth tokens, and webhook secrets; clients receive only bounded application projections.

### How should an ambiguous mutation be retried?

Persist the exact logical input and idempotency key before the first attempt. Reconcile through authoritative readback or replay the exact request with that same key before creating new work.

### What should we monitor after release?

Monitor admission-to-terminal time, stream reconnects, duplicate events, webhook delivery latency, retries, and reconciliation backlog. Alert on authorization bypass, cross-tenant disclosure, repeated conflicts, or cleanup failure.
