Pipes let your end users connect their own third-party accounts — a CRM, a Google account, a support tool — so your application can pull scoped data on their behalf. Authdog runs the OAuth2 consent flow, stores each user's tokens encrypted at rest, and refreshes them automatically, so your backend only ever asks for "a fresh access token for this user and provider."

## How it works

1. **Configure a provider** (once, per environment) in the **Pipes** console module: the OAuth `client_id` / `client_secret`, the provider's `authorize` and `token` URLs, and the scopes to request. The client secret is encrypted at rest.
2. **Send a user to consent.** Your backend asks Authdog for an authorize URL and redirects the user to it. After the user approves, the provider redirects back to your `redirect_uri` with a `code` and the `state` Authdog issued.
3. **Exchange the code.** Your backend hands the `code` + `state` back to Authdog, which exchanges them for tokens and stores them against the user's connection.
4. **Use the connection.** Whenever you need to call the provider, ask Authdog for a fresh access token; it refreshes transparently when the stored one has expired.

## Public API

All endpoints are authenticated with an environment API secret (`adenv_…`) and live at `https://api.authdog.com/pipes/v1`. The environment is resolved from the token.

| Step | Endpoint | Body / query |
| --- | --- | --- |
| Start consent | `POST /pipes/v1/authorize` | `{ provider, userId, redirectUri, scopes? }` → `{ authorizeUrl, state }` |
| Complete consent | `POST /pipes/v1/exchange` | `{ code, state, redirectUri? }` → `{ status }` |
| List a user's connections | `GET /pipes/v1/connections?userId=…` | → `{ connections }` |
| Get a fresh token | `POST /pipes/v1/token` | `{ userId, provider }` → `{ accessToken, expiresAt }` |
| Revoke | `POST /pipes/v1/revoke` | `{ userId, provider }` → `{ success }` |

```bash
# 1. Get the URL to send the user to
curl -X POST https://api.authdog.com/pipes/v1/authorize \
  -H "Authorization: Bearer $AUTHDOG_ENV_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "provider": "hubspot", "userId": "user_123", "redirectUri": "https://app.example.com/pipes/callback" }'
# → { "authorizeUrl": "https://app.hubspot.com/oauth/authorize?...", "state": "…" }

# 2. After the provider redirects back with ?code&state, exchange it
curl -X POST https://api.authdog.com/pipes/v1/exchange \
  -H "Authorization: Bearer $AUTHDOG_ENV_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "code": "…", "state": "…", "redirectUri": "https://app.example.com/pipes/callback" }'

# 3. Later, get a fresh token to call the provider
curl -X POST https://api.authdog.com/pipes/v1/token \
  -H "Authorization: Bearer $AUTHDOG_ENV_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "user_123", "provider": "hubspot" }'
# → { "accessToken": "…", "expiresAt": "2026-01-01T00:00:00.000Z" }
```

The `state` returned by `authorize` correlates the round trip — pass it back unchanged to `exchange`. Requested scopes are intersected with the provider's configured `allowedScopes` when set.

## Security

- User access and refresh tokens are AES-256-GCM encrypted at rest; the API never returns a refresh token.
- Call the Pipes API only from your backend with the environment API secret — never expose the secret to browsers or mobile clients.
- Revoking a connection deletes the stored tokens. Also revoke upstream at the provider when a user disconnects.
- Scope each provider to the minimum data your application needs.

## Related

- [Actions](/docs/actions)
- [Vault](/docs/vault)
- [Backend requests](/docs/backend)
