Integrations
Integrate Pipes
Pipes is one connection model for every third-party system your product has to reach. Your customer authorises the connection once; you make calls against a stable interface while token refresh, retries and rate limits are handled outside your codebase.
Before you begin
- A list of the systems you need to reach and what you need from each. Read-only access is far easier to get approved than write access.
- An organization to attach a connection to. Connections belong to a tenant, not to a user, so they survive the person who set them up leaving.
- A callback route to receive the authorisation redirect.
- A view on failure. A third-party system will be down or rate limited at some point, and your product should degrade rather than error.
Install
Every example on this page uses the Node.js client. The same calls exist in the Python, Go, Ruby, PHP and Java SDKs, following the naming conventions of each language.
1npm i @paycux/node
Configure
The connection redirect URI is the one new value. It must be registered before the first authorisation, and it must match exactly, including the trailing slash.
1PAYCUX_API_KEY=sk_example_7f4c1b9ad2e84c6f2PAYCUX_PIPES_REDIRECT_URI=http://localhost:3000/callback/pipes3PAYCUX_WEBHOOK_SECRET=whsec_example_2b6d9f31
Then, in the dashboard:
- 1Open Pipes and enable the connectors your product needs. Each one lists the scopes it will request.
- 2Request only the scopes you use. An over-broad scope list is the most common reason a customer's IT team refuses an integration.
- 3Register your redirect URI for each connector, and add the production URL before you ship.
- 4Set the retry and backoff policy per connector, so a slow third party does not become a queue of your own.
- 5Subscribe to connection events if you want to know when a token is revoked on the far side.
Implement
Three steps: send the customer to authorise, store the connection ID, then make calls through it. Your code never sees a third-party access token, which is the point.
Start an authorisation
Generate the link when the customer clicks connect. The state you pass comes back untouched, which is the simplest way to know which connector the person was setting up.
1import { Paycux } from "@paycux/node";2import { requirePermission } from "@/lib/authz";34const paycux = new Paycux(process.env.PAYCUX_API_KEY);56export async function POST(request: Request) {7 const { organizationId } = await requirePermission("integrations:manage");8 const { connector } = await request.json();910 const link = await paycux.pipes.createConnectLink({11 organizationId,12 connector,13 redirectUri: process.env.PAYCUX_PIPES_REDIRECT_URI,14 state: connector,15 });1617 return Response.redirect(link.url, 303);18}
Store the connection
The callback returns a connection ID. That is what you keep; there is no token for you to store, refresh or leak.
1import { Paycux } from "@paycux/node";23const paycux = new Paycux(process.env.PAYCUX_API_KEY);45export async function GET(request: Request) {6 const params = new URL(request.url).searchParams;7 const code = params.get("code");8 if (!code) return Response.redirect("/settings/integrations?error=cancelled");910 const connection = await paycux.pipes.completeConnection({ code });1112 await db.pipeConnection.upsert({13 where: {14 organizationId_connector: {15 organizationId: connection.organizationId,16 connector: connection.connector,17 },18 },19 create: {20 organizationId: connection.organizationId,21 connector: connection.connector,22 connectionId: connection.id,23 },24 update: { connectionId: connection.id },25 });2627 return Response.redirect("/settings/integrations?connected=1");28}
Make a call and survive a failure
Calls go through the connection. Handle the two failures that actually happen in production: the customer revoked access, and the far side is rate limiting you.
1import { Paycux } from "@paycux/node";23const paycux = new Paycux(process.env.PAYCUX_API_KEY);45export async function listRemoteRecords(connectionId: string, resource: string) {6 try {7 const result = await paycux.pipes.request({8 connectionId,9 method: "GET",10 path: resource,11 });1213 return { ok: true, data: result.data };14 } catch (error) {15 if (error.code === "connection_revoked") {16 await markNeedsReconnect(connectionId);17 return { ok: false, reason: "reconnect_required" };18 }1920 if (error.code === "rate_limited") {21 return { ok: false, reason: "retry_after", retryAfter: error.retryAfter };22 }2324 throw error;25 }26}
Verify
Connect for real, then break it on purpose. The reconnect path is the one your customers will meet, and it is the one most often left untested.
- 1Connect a connector against a test account you control and confirm your settings screen shows it as active.
- 2Make a read call through the connection and confirm the data comes back shaped as your code expects.
- 3Revoke the authorisation from the third-party side, then call again. Your app should mark the connection as needing reconnection rather than throwing.
- 4Reconnect and confirm the same connection record is reused, not duplicated.
- 5Open Pipes in the dashboard and check the call log for the connection: status codes, retries and rate-limit responses are all listed there.
Reference
Request and response shapes for each of these live in the API reference. Paths are relative to https://api.paycux.com.
| Method | Path | What it does |
|---|---|---|
| POST | /pipes/connect_links | Create an authorisation link for one connector and organization. |
| POST | /pipes/connections | Complete an authorisation and create the connection. |
| GET | /pipes/connections | List connections for an organization with their state. |
| GET | /pipes/connections/:id | Read one connection, including its granted scopes. |
| DELETE | /pipes/connections/:id | Disconnect and revoke the stored authorisation. |
| POST | /pipes/requests | Make a proxied call through a connection. |
| GET | /pipes/connectors | List available connectors and the scopes each requests. |