CONNECT APP
Build with Grain
Productivity
- OAuth
MCP
Give your agent Grain tools
Every Grain action is exposed as an MCP tool on Pipedream's remote server. Point a client at it with your end user's ID and Connect resolves that user's Grain account for each tool call — you store no tokens.
// accessToken: mint a short-lived token with the Connect SDK — see the MCP guide
const transport = new StreamableHTTPClientTransport(
new URL("https://remote.mcp.pipedream.net/v3"),
{
requestInit: {
headers: {
Authorization: `Bearer ${accessToken}`,
"x-pd-project-id": "{project_id}",
"x-pd-environment": "production",
"x-pd-external-user-id": "{external_user_id}", // any stable ID for this user in your system
"x-pd-app-slug": "grain",
},
},
},
)
const mcp = new Client({ name: "my-agent", version: "1.0.0" })
await mcp.connect(transport)
const { tools } = await mcp.listTools()
// e.g. run Download Recording:
const result = await mcp.callTool({
name: "grain-download-recording",
arguments: {
recordingId: "Recording ID",
syncDir: "SyncDir",
},
})# access_token: mint a short-lived token with the Connect SDK — see the MCP guide
headers = {
"Authorization": f"Bearer {access_token}",
"x-pd-project-id": "{project_id}",
"x-pd-environment": "production",
"x-pd-external-user-id": "{external_user_id}", # any stable ID for this user in your system
"x-pd-app-slug": "grain",
}
async with streamablehttp_client("https://remote.mcp.pipedream.net/v3", headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
# e.g. run Download Recording:
result = await session.call_tool("grain-download-recording", {
"recordingId": "Recording ID",
"syncDir": "SyncDir",
})API PROXY
Call the Grain API directly
For an endpoint with no pre-built tool, the Connect proxy forwards your request to the Grain API with the connected user's credentials attached. You store no tokens and write no refresh logic.
const resp = await pd.proxy.get({
externalUserId: "{external_user_id}", // any stable ID for this user in your system
accountId: "apn_xxxxxxx",
url: "https://api.grain.com/_/public-api/me",
})
// Any allowed Grain endpoint works here. Pipedream attaches the
// connected account's credentials to the outgoing request.# The path segment is the target URL, URL-safe base64 encoded:
# https://api.grain.com/_/public-api/me
curl "https://api.pipedream.com/v1/connect/{project_id}/proxy/aHR0cHM6Ly9hcGkuZ3JhaW4uY29tL18vcHVibGljLWFwaS9tZQ?external_user_id={external_user_id}&account_id=apn_xxxxxxx" \
-H "Authorization: Bearer {access_token}" \
-H "x-pd-environment: production"SDK
Run Grain actions from your backend
Connect a user's Grain account once, then run Download Recording on their behalf from your own code — TypeScript, Python, or plain HTTP.
import { PipedreamClient } from "@pipedream/sdk"
const pd = new PipedreamClient({
projectId: process.env.PIPEDREAM_PROJECT_ID!,
clientId: process.env.PIPEDREAM_CLIENT_ID!,
clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
projectEnvironment: "production",
})
const result = await pd.actions.run({
id: "grain-download-recording",
externalUserId: "{external_user_id}", // any stable ID for this user in your system
configuredProps: {
grain: { authProvisionId: "apn_xxxxxxx" },
recordingId: "Recording ID",
syncDir: "SyncDir",
},
})from pipedream import Pipedream
pd = Pipedream(
client_id="{oauth_client_id}",
client_secret="{oauth_client_secret}",
project_id="{project_id}",
project_environment="production",
)
result = pd.actions.run(
id="grain-download-recording",
external_user_id="{external_user_id}", # any stable ID for this user in your system
configured_props={
"grain": {"authProvisionId": "apn_xxxxxxx"},
"recordingId": "Recording ID",
"syncDir": "SyncDir",
},
)curl -X POST https://api.pipedream.com/v1/connect/{project_id}/actions/run \
-H "Content-Type: application/json" \
-H "X-PD-Environment: production" \
-H "Authorization: Bearer {access_token}" \
-d '{
"external_user_id": "{external_user_id}",
"id": "grain-download-recording",
"configured_props": {
"grain": { "authProvisionId": "apn_xxxxxxx" },
"recordingId": "Recording ID",
"syncDir": "SyncDir"
}
}'TOOLS
Grain actions
On-demand operations your product or agent can configure and run on behalf of a connected user.
-
Download Recording
actionDownloads a Grain recording's media file (video or audio) and writes it to File Stash, returningfilePath,filename, andcontentType. Only recordings with processed media have a downloadable file — checkmedia_type(videooraudio, nottranscript) from List Recordings or Get Recording first. Use Get Transcript instead if you only need the spoken content, not the media file itself. Example:recordingId: "8a089fcb-0961-4393-8da2-f0db5f8cfd79"downloads the file and returns{"filePath": "/tmp/grain-recording-8a089fcb-....mp4", "filename": "grain-recording-8a089fcb-....mp4", "contentType": "video/mp4"}. See the documentationRead-onlyv0.0.1 -
Get Recording
actionFetches a specific recording by its ID from Grain, returning its metadata (title, times, URL, tags, teams, meeting type). Enable the optional include props to add highlights, participants, AI action items, AI summary, calendar event, HubSpot data, or screenshares to the response. Use List Recordings to find recording IDs, and Get Transcript to fetch the full transcript. Example:recordingId: "pppp6666-qq77-rr88-ss99-tttt00000000"withaiSummary: truereturns the recording's metadata plus{"ai_summary": {"text": "..."}}. See the documentationRead-onlyv1.0.2 -
Get Transcript
actionFetches the full transcript of a Grain recording. Thejsonformat returns structured segments with speaker, participant ID, start/end times in milliseconds, and text;txt,vtt, andsrtreturn plain text or subtitle formats. Use List Recordings to find recording IDs; use Get Recording for the recording's metadata instead of its transcript. Example:recordingId: "pppp6666-qq77-rr88-ss99-tttt00000000"withformat: "txt"returns plain text like"Speaker 1: Thanks for joining today...". See the documentationRead-onlyv0.0.3 -
List Meeting Types
actionLists the meeting types configured in your Grain workspace (id, name, scope), where scope isinternalorexternal. Use this to understand how recordings are categorized, or to resolve a meeting type's ID for filtering. Example: returns[{"id": "e8b894a9-7ecf-4330-a282-527c085618fd", "name": "Sales", "scope": "external"}, {"id": "815747d1-9e25-40f6-a3b8-e1908e25151e", "name": "1:1s", "scope": "internal"}]. See the documentationRead-onlyv0.0.1 -
List Recordings
actionLists Grain recordings, optionally filtered by start datetime range (ISO8601), title search, participant scope, team, or meeting type. Automatically paginates and returns up to Max Results recordings. Use this to find recording IDs for Get Recording and Get Transcript. Example:titleSearch: "Acme"returns recordings like[{"id": "pppp6666-qq77-rr88-ss99-tttt00000000", "title": "Acme Renewal Call", "start_datetime": "2026-01-05T15:00:00Z", "media_type": "video", ...}]. Passfieldsto return only the fields you need instead of the full object. See the documentationRead-onlyv0.1.0 -
List Teams
actionLists the teams in your Grain workspace (id, name). Use this to resolve a team's ID before sharing a recording with it via Manage Recording Sharing. Example: returns[{"id": "a414c333-c9fe-4fdc-9131-fb31796699b2", "name": "Pipedream"}]. See the documentationRead-onlyv0.0.1 -
List Users
actionLists the users in your Grain workspace (id, name, email). Use this to resolve a user's ID before sharing a recording with them via Manage Recording Sharing, or to identify who's who when reviewing recording participants. Example: returns[{"id": "d91b7ed0-a149-425c-9623-0664148e4fc1", "name": "Danny Archer", "email": "darcher@pipedream.com"}]. See the documentationRead-onlyv0.0.1 -
Manage Recording Sharing
actionShares or unshares a recording with a specific user or team. Use List Recordings to find the recording's ID, and List Users or List Teams to resolve the target's ID. Setoperationtoshareto grant access orunshareto revoke it, andtargetTypetouserorteam. Example:recordingId: "pppp6666-qq77-rr88-ss99-tttt00000000", operation: "share", targetType: "user", targetId: "d91b7ed0-a149-425c-9623-0664148e4fc1"shares the recording with that user and returns{"success": true}. See the documentationWritev0.0.1 -
Update Recording
actionRenames a recording and/or adds and removes tags on it. All fields are optional — pass only what you want to change. Use List Recordings first to find the recording's ID. Example: to retitle a recording and tag it, passtitleandaddTags: ["qa-verified"]together. See the documentationWritev0.0.1
-
New Highlight (Instant)
triggerEmit new event when a highlight is added. See the documentationInstantv1.0.1 -
New Highlight Removed (Instant)
triggerEmit new event when a highlight is removed. See the documentationInstantv1.0.1 -
New Highlight Updated (Instant)
triggerEmit new event when a highlight is updated. Deduplicates retried webhook deliveries of the same update; each distinct update still emits. See the documentationInstantv1.1.0 -
New Recording (Instant)
triggerEmit new event when a recording is added. See the documentationInstantv1.0.1 -
New Recording Removed (Instant)
triggerEmit new event when a recording is removed. See the documentationInstantv1.0.1 -
New Recording Updated (Instant)
triggerEmit new event when a recording is updated. Deduplicates retried webhook deliveries of the same update; each distinct update still emits. See the documentationInstantv1.1.0 -
New Story (Instant)
triggerEmit new event when a story is added. See the documentationInstantv1.0.1 -
New Story Removed (Instant)
triggerEmit new event when a story is removed. See the documentationInstantv1.0.1 -
New Story Updated (Instant)
triggerEmit new event when a story is updated. Deduplicates retried webhook deliveries of the same update; each distinct update still emits. See the documentationInstantv1.1.0
MULTI-APP
Use Grain with other popular apps
Most products don't stop at one integration. Pair Grain with the other apps your users rely on, and ship use cases that span both.
- App slug
- grain
- Authentication
- OAuth
- Categories
- Productivity
- Actions
- 9
- Triggers
- 9
- API proxy
- Available