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

# Submit source credentials

> Resume a run that paused to ask for a live source's API credentials — the secure path for secrets, kept out of the agent thread.

```http theme={null}
POST https://app.vern.so/api/v1/migrations/{migration_id}/runs/{run_id}/credentials
```

When a run extracts from a [live source](/migration-api/source-connection) whose
connector still needs secrets, the [managed agent](/migration-api/concepts#the-managed-agent)
pauses and asks for them. The run reports
[`status: "blocked"` with `blocked_reason: "credentials"`](/migration-api/poll-a-run#a-credential-block),
and this endpoint is the **only** way to answer it.

<Warning>
  Never send credentials as a [message
  answer](/migration-api/answer-questions) — the `messages` endpoint rejects a
  credential-blocked run with `409` for exactly this reason. Secrets sent here go
  straight to a secured vault and never enter the agent thread, the run row, or
  logs; the agent resumes with a sanitized confirmation only.
</Warning>

`{migration_id}` and `{run_id}` come from
[starting](/migration-api/start-a-run) and [polling](/migration-api/poll-a-run)
the run.

## Authentication

Requires an `x-api-key` header. See [Authentication](/migration-api/authentication).

## What you're answering

A credential-blocked poll response tells you exactly what to collect:

```json theme={null}
{
  "run_id": "d4c3b2a1-...",
  "status": "blocked",
  "blocked_reason": "credentials",
  "credential_request": {
    "connection_id": "9b1f2c3d-...",
    "source_key": "salesforce",
    "source_name": "Salesforce",
    "reason": "I need API access to read your contacts.",
    "guidance": {
      "summary": "Create a connected app and copy its OAuth credentials.",
      "steps": ["Open Setup → App Manager", "New Connected App", "Copy the consumer key and secret"],
      "links": [{ "label": "Salesforce docs", "url": "https://help.salesforce.com/…" }]
    },
    "schema": { "client_id": { "type": "string" }, "client_secret": { "type": "string", "secret": true } }
  }
}
```

Render a form from `credential_request.schema`, show the `guidance`, and post the
collected values back.

## Request body

<ParamField body="credentials" type="object" required>
  The secret values, keyed to match the request's `schema` (e.g.
  `{ "client_id": "…", "client_secret": "…" }`). Must be a non-empty object.
</ParamField>

<ParamField body="connection_id" type="string">
  Optional guard. If sent, it must match the `credential_request.connection_id`
  the run asked about, or the call returns `409`. The connection is always taken
  from the run itself — a key can only ever fill the connection the agent
  actually requested.
</ParamField>

## Response

`200 OK`

```json theme={null}
{
  "run_id": "d4c3b2a1-9f8e-47d6-b5a4-c3d2e1f0a9b8",
  "status": "running"
}
```

The secrets are stored and the run resumes. Keep
[polling](/migration-api/poll-a-run) until it's terminal. If the connector
rejects the credentials, the run blocks for credentials again — **re-submit**
through this same endpoint (it overwrites the stored secret) and the agent
retries the extractor.

## Errors

| Status | Meaning                                                                                                                                                                                     |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `credentials` missing or not a non-empty object; malformed JSON.                                                                                                                            |
| `401`  | API key missing or invalid.                                                                                                                                                                 |
| `404`  | No such run, the migration isn't in your account, or the run's connection no longer exists.                                                                                                 |
| `409`  | The run isn't awaiting credentials (not `blocked`, or blocked on a question instead); `connection_id` doesn't match the run; or no active credential request to answer yet — retry shortly. |
| `429`  | Rate limit hit — back off and retry.                                                                                                                                                        |
| `500`  | Server error.                                                                                                                                                                               |

## Example

```bash theme={null}
curl -X POST \
  https://app.vern.so/api/v1/migrations/c0a8012e-.../runs/d4c3b2a1-.../credentials \
  -H "x-api-key: $VERN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credentials": { "client_id": "…", "client_secret": "…", "refresh_token": "…" }
  }'
```

## Next

* [Poll a run](/migration-api/poll-a-run) — watch the resumed extraction.
* [Connect a live source](/migration-api/source-connection) — manage the connection a credential request belongs to.
* [Answer the agent](/migration-api/answer-questions) — the non-secret question path.
