# Secret-backed integrations

Use this guide after `integration_bootstrap` when the integration uses one or
more saved secrets instead of an OAuth integration.

This is the default path for many automation-oriented integrations:

- API keys
- personal access tokens
- account IDs plus tokens
- static credentials that the user can copy from a provider dashboard

## Goal

Keep the integration flow simple:

1. research the provider's auth requirements (see step 1 below)
2. collect the required secret values through `/account/secrets/new`
3. run one real authenticated smoke test
4. only then build the downstream package or workflow

Do **not** jump straight to a package app or saved package if the secret and
smoke-test path is unclear.

## Default recipe

1. Identify the provider's auth contract.
   - When the contract is unfamiliar, read the provider's official docs and
     prefer `communitySearch` for a close helpers package before collecting
     secrets. Verify every URL against the provider's own domain (see
     `integration_bootstrap` for the full trust caveat).
   - Confirm which fields are secrets and which are readable config.
   - Prefer the provider's native credential shape when possible.
   - If the API also needs readable configuration such as an account ID, base
     URL, region, workspace slug, or default sender, store those in memories,
     `packageStorage()`, or a repo — not as secrets.
2. Check whether the needed secrets already exist.
   - Use `search` first for saved secret references.
   - Use `kody.secretList({})` inside `execute` only when you need the current
     runtime metadata.
3. If any secret is missing, stop and send the user to `/account/secrets/new`.
   - Ask for each missing secret by name.
   - Include the provider dashboard URL and short creation steps when helpful.
   - Do **not** ask the user to paste the secret into chat.
4. Wait for the user to confirm the secret is saved.
   - Do not treat the connect URL alone as completion.
5. Run one cheap authenticated smoke test in `execute`.
   - Use the same secret names and request shape the final package will use.
   - Prefer a small read-only endpoint such as account info, profile info, or a
     single-item list endpoint.
6. If the smoke test is blocked on host approval, stop.
   - Surface the approval link from the error.
   - Wait for the user to approve the host.
   - Retry only after approval.
7. After the smoke test passes, build the dependent package or workflow.
   - Prefer plain package exports for simple automations.
   - Use a package app only when the user actually needs interactive UI,
     browser-side forms, or hosted callbacks.
8. After the package is saved or published, finish package secret approval when
   needed.
   - Self-authored packages and adopted forks (`communityForkAdopt`) get
     automatic read/use access to user secrets (mutations still need an
     `allowed_packages` grant); unadopted community forks still need explicit
     package approval for read/use, or adoption after review.
   - An ad hoc `execute` smoke test does **not** grant package secret access for
     community forks.
   - Read `pending_secret_package_approvals` from `packageSave` or
     `packagePublishExternalPush` (null for self-authored / adopted packages).
   - When present, either review the source and call `communityForkAdopt`, or
     send `bulk_approval_url` / each `approval_url`.
   - Wait for the user to approve or for adoption (when required), then verify
     from `execute` with a static `kody:@scope/package/export` import before
     treating the package as complete. Prefer a read-only export or a
     package-supported dry-run input that actually reads the approved secret
     (for example an authenticated read-only API call), so verification proves
     secret access without external side effects. Secret mounts bind in the
     package's own surfaces (jobs, apps, subscriptions, HTTP invocation).

## Secret names and readable config

Use descriptive names that reflect the real auth contract:

- good secret names:
  - `providerApiKey`
  - `providerAccessToken`
  - `providerAccountToken`
- good places for readable identifiers and defaults (account id, region,
  sender):
  - memories for durable facts the agent should recall
  - `packageStorage()` for package runtime knobs
  - a repo for versioned calibration or documents

If the auth contract has multiple fields, save only the truly sensitive fields
as secrets. Keep readable identifiers and defaults out of the secret store.

## Using a saved secret in `fetch`

Saved secrets are referenced by **placeholder**, never by plaintext. Inside
`execute` (and package code), put `{{secret:name}}` — or
`{{secret:name|scope=user}}` to pin a scope — in the URL, headers, or body of an
outbound `fetch`. Kody resolves the placeholder for **approved** hosts only:

```ts
const response = await fetch('https://api.example.com/v1/me', {
	headers: {
		Authorization: 'Bearer {{secret:providerAccessToken}}',
	},
})
```

Rules:

- `kody.secretList({})` returns metadata only (names, allowed hosts) — use it to
  find the right secret name, then reference that name in a placeholder.
- Placeholders only resolve in secret-aware `fetch` paths; they are not general
  string interpolation.
- Never echo a resolvable literal placeholder into chat, logs, issue bodies, or
  any content that may later be sent over `fetch`. To mention the syntax in
  prose, use the inert `{{secret:<name>}}` form — angle brackets are outside the
  name charset, so it never resolves. To deliberately deliver a resolvable
  placeholder to a third party, set the `x-kody-secret-resolution: off` header
  on that `fetch` (the gateway strips it and skips resolution for that request).
- For Basic Auth derived from two secrets, use `secretHeaders.basic(...)` from
  `kody:runtime` (see the secrets usage docs).

## When `/account/secrets/new` is enough

In the common case, `/account/secrets/new` is the whole setup surface.

Use it when:

- the provider gives the user one or more static secret values
- the final request can use those secrets directly in `fetch(...)`
- the only extra work after saving the secret is host approval and a smoke test

This should be the default assumption for non-OAuth integrations.

## When to avoid package apps

A package app is **not** the default integration path.

Do **not** build one just to:

- collect a normal API key or token
- collect an account ID or other readable config
- work around the need to ask the user for a secret through
  `/account/secrets/new`

A package app is the exception when the setup requires something
`/account/secrets/new` cannot express cleanly, such as:

- browser-side OAuth or hosted callback handling
- a provider-specific setup wizard with multiple non-secret choices
- a required transformation step that cannot be represented by saving the raw
  secret plus readable config directly

Even then, keep the UI focused on setup. The downstream package should wait for
the post-setup smoke test.

## Recommended chat phrasing

For a new secret-backed integration, the default response shape is:

1. state the auth requirement you found
2. ask the user to save the required secret or secrets through
   `/account/secrets/new`
3. say you will run a smoke test after they confirm setup
4. say you will build the package only after the smoke test passes

Example:

- \"This API uses an account ID plus a token. Please save `providerToken`
  through `/account/secrets/new`. I will keep `providerAccountId` as readable
  config (memory or package storage), run a real authenticated smoke test, and
  then build the package.\"

## Anti-patterns

Avoid these mistakes:

- building a package app before checking whether `/account/secrets/new` is
  enough
- saving readable config as a secret
- saving the downstream package before the smoke test passes
- assuming a saved secret automatically approves outbound hosts
- treating an ad hoc `execute` smoke test as package secret approval for a
  community-forked package
- marking an unadopted community-forked secret-using package complete without
  adopting after review (`communityForkAdopt`) or sending package approval links
  (prefer the bulk approval URL when multiple secrets need access)
- inventing a provider-specific flow when one or two secrets plus a smoke test
  would do
