YouTube Integration
Connect your agent to a YouTube channel to upload and publish videos and Shorts, edit their details and playlists, list the channel's videos, report performance, and pick video assets for Google Ads Video/Demand Gen campaigns.
Overview
The YouTube integration uses Google OAuth 2.0 with:
- YouTube Data API v3: channel info, video listings, uploads, video details, playlists, and thumbnails
- YouTube Analytics API v2: view counts, watch time, subscriber metrics
Skills that use this integration:
int-youtube-manage: Uploads and publishes videos and Shorts (title, description, tags, visibility or a scheduled release, playlist, thumbnail), lists channel videos, edits video details and playlists, reports performance, and picks video assets for Google Ads Video/Demand Gen campaigns
Agents that typically enable this integration:
- Video Creator: to publish the videos it makes to your channel. Add
int-youtube-managewithSkillsApply; it adds $1 a month to the Starter price. Video Creator then publishes a finished video when the request asks for it or aYouTubeline in itsvideo-preferencessays to, and otherwise offers to. - Google Ads Manager: for creating Video and Demand Gen campaigns that reference existing YouTube videos
Availability
| Mode | Status | Notes |
|---|---|---|
"own" | Available | Your own Google Cloud project and OAuth client. |
"wiro" | Coming soon | Wiro's shared Google Cloud project is awaiting Google's review. Until then, OAuthConnect in Wiro mode, which is also what you get when you leave out authmethod, returns Wiro-managed OAuth is not enabled for YouTube. |
Prerequisites
- A Wiro API key (see Authentication).
- A deployed agent (see Agent Overview).
- A YouTube channel the connecting Google account owns or manages.
- A Google Cloud project with YouTube Data API v3 and YouTube Analytics API enabled.
- An HTTPS callback URL.
Connect
Step 1: Create a Google Cloud project and enable the YouTube APIs
- console.cloud.google.com → create a project.
- APIs & Services → Library → enable:
- OAuth consent screen:
- External user type for multi-tenant use
- App name, support email
- Add scopes:
youtube,youtube.readonly,yt-analytics.readonly - Publish the app (In production). While it is in Testing, Google expires its refresh tokens after 7 days and the agent loses the channel.
Step 2: Create the OAuth client
APIs & Services → Credentials → Create Credentials → OAuth client ID:
- Application type: Web application
- Authorized redirect URIs:
https://api.wiro.ai/v1/UserAgentOAuth/YTCallback
Copy the client ID and the client secret. If you missed the secret, open the client and use Reset secret.
Step 3: Save the OAuth client to Wiro
curl -X POST "https://api.wiro.ai/v1/UserAgent/CredentialUpsert" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"fields": [
{ "credentialkey": "youtube", "fieldname": "clientid", "fieldvalue": "123-abc.apps.googleusercontent.com" },
{ "credentialkey": "youtube", "fieldname": "clientsecret", "fieldvalue": "YOUR_CLIENT_SECRET" },
{ "credentialkey": "youtube", "fieldname": "authmethod", "fieldvalue": "own" }
]
}'
Step 4: Start the connection
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/OAuthConnect" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "youtube",
"redirecturl": "https://your-app.com/settings/integrations",
"authmethod": "own"
}'
Send the user to the returned authorizeUrl.
Step 5: Save the channel selection
After consent the user returns with ?yt_connected=true&yt_channels=<URL-encoded-JSON>. Each entry is { id, title, customUrl, subscriberCount }: id is the channel ID, title the channel name, customUrl its custom URL or handle (empty when it has none), and subscriberCount a string ("0" when YouTube returns no count). When the Google account has a single channel, Wiro selects it for you. Otherwise map the user's choice to { channelid, channeltitle } (id becomes channelid, title becomes channeltitle) and call SetPickerAccounts:
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/SetPickerAccounts" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "youtube",
"accounts": [
{ "channelid": "UC...", "channeltitle": "My Channel" }
]
}'
Pass multiple { channelid, channeltitle } entries to authorize the agent against several channels at once.
Disconnect
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/OAuthDisconnect" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "youtube"
}'
Status
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/OAuthStatus" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "youtube"
}'
Response:
{
"result": true,
"connected": true,
"accounts": [
{ "id": "UC...", "name": "My Channel" }
],
"connectedat": "2026-04-17T12:00:00.000Z",
"tokenexpiresat": "2026-04-17T13:00:00.000Z",
"errors": []
}
accounts[] carries one entry per selected channel (id = channelid, name = channeltitle).
API Reference
POST /UserAgentOAuth/OAuthConnect
| Parameter | Type | Required | Description |
|---|---|---|---|
useragentguid | string | Yes | Agent instance GUID. |
credentialkey | string | Yes | "youtube". |
redirecturl | string | Yes | Any HTTPS URL you control (or http://localhost / http://127.0.0.1 for development). |
authmethod | string | No | "own". Omitted, it means "wiro", which is not available yet. |
GET /UserAgentOAuth/YTCallback
Server-side endpoint invoked by Google. You don't call it: you register it as the Authorized redirect URI of your OAuth client. Wiro then sends the browser to your redirecturl with yt_connected=true&yt_channels=<JSON> (a URL-encoded array of { id, title, customUrl, subscriberCount }) or yt_error=<code>. The callback path is per-provider: YouTube's stays YTCallback. When you pass a channel to SetPickerAccounts, map it to { channelid: id, channeltitle: title }.
POST /UserAgentOAuth/SetPickerAccounts
| Parameter | Type | Required | Description |
|---|---|---|---|
useragentguid | string | Yes | Agent instance GUID. |
credentialkey | string | Yes | "youtube". |
accounts | array | Yes | One or more { channelid, channeltitle } entries. |
Response: { result, accounts: [{id, name}, ...], errors }. Triggers agent restart if running.
POST /UserAgentOAuth/OAuthStatus
Body: { useragentguid, credentialkey: "youtube" }. Response: connected, accounts: [{id, name}, ...] (one entry per selected channel: id = channelid, name = channeltitle), connectedat, tokenexpiresat.
POST /UserAgentOAuth/OAuthDisconnect
Body: { useragentguid, credentialkey: "youtube" }. Clears the tokens and the selected channels in Wiro. Your Client ID and Client Secret are kept. Wiro does not ask Google to revoke the authorization.
What the agent does with this integration
Uploading and publishing videos
Ask the agent to put a video on YouTube, for example one it made with Video Creator. It uploads the video to the selected channel and replies with the link.
- Regular videos and Shorts. One upload covers both: YouTube files a vertical (9:16) or square video of up to 3 minutes as a Short and anything else as a regular video. A Short needs no
#Shortstag. - Details. Title (up to 100 characters), description (up to 5,000), tags, category, language, the made-for-kids setting, and visibility: public, unlisted, private, or a scheduled release time. A video that shows realistic AI-generated people or scenes is marked as altered or synthetic content, and YouTube labels it.
- Playlist and thumbnail. The agent can add the video to a playlist and set a frame of it as the thumbnail. YouTube accepts custom thumbnails for regular videos on verified channels.
- Approval. Uploads go through the agent's approval policy (the
approval-policycustom skill). A request that already says to upload or publish is the approval; otherwise the agent shows the title, description, tags, playlist and visibility first and uploads after you approve. - Drafts. Ask for a private or unlisted upload, and ask the agent to publish it later: it makes the video public, or schedules it, and keeps the synthetic-content label.
- No duplicates. When an upload fails, the agent looks for the video among the channel's uploads before it tries once more.
The reply gives the video's link, its visibility or scheduled time, and its playlist.
Videos from an unaudited Google Cloud project stay private. YouTube restricts videos uploaded through a Google Cloud project created after 28 July 2020 that has not passed the YouTube API audit to private viewing, whatever visibility was asked for. The agent reads the video back after the upload; if YouTube has locked it, the agent tells you once and does not upload it again. Request the audit for the project behind your OAuth client with YouTube's API audit form. Until it passes, download the video from the chat and upload it in YouTube Studio.
Channel video listing
Agent lists channel uploads:
Operator → "show my last 20 YouTube videos"
Agent → GET /youtube/v3/playlistItems?playlistId=UU{channel}
returns 20 items with video IDs, titles, dates, durations
Video asset for Google Ads campaigns
Google Ads Manager agent uses this skill to pick video assets for Video and Demand Gen campaigns:
Agent → /youtube-manage list last 30 days of videos
→ picks top 3 by views
→ /googleads-manage create Video campaign with these as creatives
Performance analytics
Agent queries video-level metrics:
Operator → "which videos performed best last month?"
Agent → POST /youtubeAnalytics/v2/reports
with dimensions=[video], metrics=[views, averageViewDuration, estimatedMinutesWatched]
Skill reference
- agent-skills.md: custom skill schema and the
int-youtube-managekey
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Wiro-managed OAuth is not enabled for YouTube in OAuthConnect's errors[] | Wiro mode isn't available yet, and it is the default when authmethod is left out | Save your OAuth client (Step 3) and call OAuthConnect with "authmethod": "own" |
YouTube credentials not configured | authmethod: "own" without a saved clientid and clientsecret | Save them (Step 3), then retry |
yt_error=no_accounts on your redirecturl | Google listed no YouTube channel for this account, and Wiro saved nothing: the account has no channel, or the YouTube Data API v3 is not enabled in your project | Enable the API (Step 1), or have the user create a channel, then call OAuthConnect again |
quotaExceeded | Daily quota hit | Wait 24h or request higher quota from Google |
channelNotFound | Channel deleted or moved | User re-selects via SetPickerAccounts |
invalid_grant | Refresh token expired or revoked; a consent screen still in Testing expires it after 7 days | Publish the consent screen, then re-connect via OAuthConnect |
| A video uploaded as public or unlisted stays private | The Google Cloud project hasn't passed the YouTube API audit | Request the audit; until then upload the video in YouTube Studio |
redirect_uri_mismatch on Google's page
The Authorized redirect URIs of your OAuth client don't include https://api.wiro.ai/v1/UserAgentOAuth/YTCallback exactly as written. Google stops on its own error page, so nothing comes back to your redirecturl. Add the URI (Step 2) and connect again.