Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions apps/docs/content/docs/en/blocks/credential.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
title: Credential
---

import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'

The Credential block has two operations: **Select Credential** picks a single OAuth credential and outputs its ID reference for downstream blocks; **List Credentials** returns all OAuth credentials in the workspace (optionally filtered by provider) as an array for iteration.

<div className="flex justify-center">
<Image
src="/static/blocks/credential.png"
alt="Credential Block"
width={400}
height={300}
className="my-6"
/>
</div>

<Callout>
The Credential block outputs credential **ID references**, not secrets. Downstream blocks receive the ID and resolve the actual OAuth token securely during their own execution.
</Callout>

## Configuration Options

### Operation

| Value | Description |
|---|---|
| **Select Credential** | Pick one OAuth credential and output its reference — use this to wire a single credential into downstream blocks |
| **List Credentials** | Return all OAuth credentials in the workspace as an array — use this with a ForEach loop |

### Credential (Select operation)

Select an OAuth credential from your workspace. The dropdown shows all connected OAuth accounts (Google, GitHub, Slack, etc.).

In advanced mode, paste a credential ID directly. You can copy a credential ID from your workspace's Credentials settings page.

### Provider (List operation)

Filter the returned OAuth credentials by provider. Select one or more providers from the dropdown — only providers you have credentials for will appear. Leave empty to return all OAuth credentials.

| Example | Returns |
|---|---|
| Gmail | Gmail credentials only |
| Slack | Slack credentials only |
| Gmail + Slack | Gmail and Slack credentials |

## Outputs

<Tabs items={['Select Credential', 'List Credentials']}>
<Tab>
| Output | Type | Description |
|---|---|---|
| `credentialId` | `string` | The credential ID — pipe this into other blocks' credential fields |
| `displayName` | `string` | Human-readable name (e.g. "waleed@company.com") |
| `providerId` | `string` | OAuth provider ID (e.g. `google-email`, `slack`) |
</Tab>
<Tab>
| Output | Type | Description |
|---|---|---|
| `credentials` | `json` | Array of OAuth credential objects (see shape below) |
| `count` | `number` | Number of credentials returned |

Each object in the `credentials` array:

| Field | Type | Description |
|---|---|---|
| `credentialId` | `string` | The credential ID |
| `displayName` | `string` | Human-readable name |
| `providerId` | `string` | OAuth provider ID |
</Tab>
</Tabs>

## Example Use Cases

**Shared credential across multiple blocks** — Define once, use everywhere
```
Credential (Select, Google) → Gmail (Send) & Google Drive (Upload) & Google Calendar (Create)
```

**Multi-account workflows** — Route to different credentials based on logic
```
Agent (Determine account) → Condition → Credential A or Credential B → Slack (Post)
```

**Iterate over all Gmail accounts**
```
Credential (List, Provider: Gmail) → ForEach Loop → Gmail (Send) using <loop.currentItem.credentialId>
```

<div className="flex justify-center">
<Image
src="/static/blocks/credential-loop.png"
alt="Credential List wired into a ForEach Loop"
width={900}
height={400}
className="my-6"
/>
</div>

## How to wire a Credential block

### Select Credential

1. Drop a **Credential** block and select your OAuth credential from the picker
2. In the downstream block, switch to **advanced mode** on its credential field
3. Enter `<credentialBlockName.credentialId>` as the value

<Tabs items={['Gmail', 'Slack']}>
<Tab>
In the Gmail block's credential field (advanced mode):
```
<myCredential.credentialId>
```
</Tab>
<Tab>
In the Slack block's credential field (advanced mode):
```
<myCredential.credentialId>
```
</Tab>
</Tabs>

### List Credentials

1. Drop a **Credential** block, set Operation to **List Credentials**
2. Optionally select one or more **Providers** to narrow results (only your connected providers appear)
3. Wire `<credentialBlockName.credentials>` into a **ForEach Loop** as the items source
4. Inside the loop, reference `<loop.currentItem.credentialId>` in downstream blocks' credential fields

## Best Practices

- **Define once, reference many times**: When five blocks use the same Google account, use one Credential block and wire all five to `<credential.credentialId>` instead of selecting the account five times
- **Outputs are safe to log**: The `credentialId` output is a UUID reference, not a secret. It is safe to inspect in execution logs
- **Use for environment switching**: Pair with a Condition block to route to a production or staging OAuth credential based on a workflow variable
- **Advanced mode is required**: Downstream blocks must be in advanced mode on their credential field to accept a dynamic reference
- **Use List + ForEach for fan-out**: When you need to run the same action across all accounts of a provider, List Credentials feeds naturally into a ForEach loop
- **Narrow by provider**: Use the Provider multiselect to filter to specific services — only providers you have credentials for are shown

<FAQ items={[
{ question: "Does the Credential block expose my secret or token?", answer: "No. The block outputs a credential ID (a UUID), not the actual OAuth token. Downstream blocks receive the ID and resolve the token securely in their own execution context. Secrets never appear in workflow state, logs, or the canvas." },
{ question: "What credential types does it support?", answer: "OAuth connected accounts only (Google, GitHub, Slack, etc.). Environment variables and service accounts cannot be resolved by ID in downstream blocks, so they are not supported." },
{ question: "How is Select different from just copying a credential ID into advanced mode?", answer: "Functionally identical — both pass the same credential ID to the downstream block. The Credential block adds value when you need to use one credential in many blocks (change it once), or when you want to select between credentials dynamically using a Condition block." },
{ question: "Can I list all OAuth credentials in my workspace?", answer: "Yes. Set the Operation to 'List Credentials'. Optionally filter by provider using the Provider multiselect. Wire the credentials output into a ForEach loop to process each credential individually." },
{ question: "Can I use a Credential block output in a Function block?", answer: "Yes. Reference <credential.credentialId> in your Function block's code. Note that the function will receive the raw UUID string — if you need the resolved token, the downstream block must handle the resolution (as integration blocks do). The Function block does not automatically resolve credential IDs." },
{ question: "What happens if the credential is deleted?", answer: "The Select operation will throw an error at execution time: 'Credential not found'. The List operation will simply omit the deleted credential from the results. Update the Credential block to select a valid credential before re-running." },
]} />
1 change: 1 addition & 0 deletions apps/docs/content/docs/en/blocks/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"agent",
"api",
"condition",
"credential",
"evaluator",
"function",
"guardrails",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docs/public/static/blocks/credential.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,10 @@ interface ComboBoxProps {
/** Configuration for the sub-block */
config: SubBlockConfig
/** Async function to fetch options dynamically */
fetchOptions?: (
blockId: string,
subBlockId: string
) => Promise<Array<{ label: string; id: string }>>
fetchOptions?: (blockId: string) => Promise<Array<{ label: string; id: string }>>
/** Async function to fetch a single option's label by ID (for hydration) */
fetchOptionById?: (
blockId: string,
subBlockId: string,
optionId: string
) => Promise<{ label: string; id: string } | null>
/** Field dependencies that trigger option refetch when changed */
Expand Down Expand Up @@ -135,7 +131,7 @@ export const ComboBox = memo(function ComboBox({
setIsLoadingOptions(true)
setFetchError(null)
try {
const options = await fetchOptions(blockId, subBlockId)
const options = await fetchOptions(blockId)
setFetchedOptions(options)
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to fetch options'
Expand All @@ -144,7 +140,7 @@ export const ComboBox = memo(function ComboBox({
} finally {
setIsLoadingOptions(false)
}
}, [fetchOptions, blockId, subBlockId, isPreview, disabled])
}, [fetchOptions, blockId, isPreview, disabled])

// Determine the active value based on mode (preview vs. controlled vs. store)
const value = isPreview ? previewValue : propValue !== undefined ? propValue : storeValue
Expand Down Expand Up @@ -363,7 +359,7 @@ export const ComboBox = memo(function ComboBox({
let isActive = true

// Fetch the hydrated option
fetchOptionById(blockId, subBlockId, valueToHydrate)
fetchOptionById(blockId, valueToHydrate)
.then((option) => {
if (isActive) setHydratedOption(option)
})
Expand All @@ -378,7 +374,6 @@ export const ComboBox = memo(function ComboBox({
fetchOptionById,
value,
blockId,
subBlockId,
isPreview,
disabled,
fetchedOptions,
Expand Down
Loading
Loading