Skip to content

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.

terminal
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.

.env.local
1PAYCUX_API_KEY=sk_example_7f4c1b9ad2e84c6f
2PAYCUX_PIPES_REDIRECT_URI=http://localhost:3000/callback/pipes
3PAYCUX_WEBHOOK_SECRET=whsec_example_2b6d9f31

Then, in the dashboard:

  1. 1Open Pipes and enable the connectors your product needs. Each one lists the scopes it will request.
  2. 2Request only the scopes you use. An over-broad scope list is the most common reason a customer's IT team refuses an integration.
  3. 3Register your redirect URI for each connector, and add the production URL before you ship.
  4. 4Set the retry and backoff policy per connector, so a slow third party does not become a queue of your own.
  5. 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.

app/api/pipes/connect/route.ts
1import { Paycux } from "@paycux/node";
2import { requirePermission } from "@/lib/authz";
3
4const paycux = new Paycux(process.env.PAYCUX_API_KEY);
5
6export async function POST(request: Request) {
7 const { organizationId } = await requirePermission("integrations:manage");
8 const { connector } = await request.json();
9
10 const link = await paycux.pipes.createConnectLink({
11 organizationId,
12 connector,
13 redirectUri: process.env.PAYCUX_PIPES_REDIRECT_URI,
14 state: connector,
15 });
16
17 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.

app/callback/pipes/route.ts
1import { Paycux } from "@paycux/node";
2
3const paycux = new Paycux(process.env.PAYCUX_API_KEY);
4
5export 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");
9
10 const connection = await paycux.pipes.completeConnection({ code });
11
12 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 });
26
27 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.

lib/pipes.ts
1import { Paycux } from "@paycux/node";
2
3const paycux = new Paycux(process.env.PAYCUX_API_KEY);
4
5export 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 });
12
13 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 }
19
20 if (error.code === "rate_limited") {
21 return { ok: false, reason: "retry_after", retryAfter: error.retryAfter };
22 }
23
24 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.

  1. 1Connect a connector against a test account you control and confirm your settings screen shows it as active.
  2. 2Make a read call through the connection and confirm the data comes back shaped as your code expects.
  3. 3Revoke the authorisation from the third-party side, then call again. Your app should mark the connection as needing reconnection rather than throwing.
  4. 4Reconnect and confirm the same connection record is reused, not duplicated.
  5. 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.

MethodPathWhat it does
POST/pipes/connect_linksCreate an authorisation link for one connector and organization.
POST/pipes/connectionsComplete an authorisation and create the connection.
GET/pipes/connectionsList connections for an organization with their state.
GET/pipes/connections/:idRead one connection, including its granted scopes.
DELETE/pipes/connections/:idDisconnect and revoke the stored authorisation.
POST/pipes/requestsMake a proxied call through a connection.
GET/pipes/connectorsList available connectors and the scopes each requests.

Next steps