space.hooks.connections

Join sources to destinations, with a filter, a delay, and a transformation.

A connection sends the webhooks a source receives to one destination. A source can have many connections, so one webhook can go to several destinations, and a destination can take webhooks from several sources.

When a webhook arrives, each enabled connection of its source checks its filter. Each one that matches makes a delivery to its destination, due after the connection’s delay. An event that no connection took is filtered.

A project holds at most 200 connections. Every call below can also fail with the codes shared by all of space, such as missing_project, forbidden, timeout, or network_error. See SpaceError.

Filters

A filter is a JSON object that says which webhooks the connection takes. It matches on four parts of the webhook as it was received:

  • body, parsed as JSON, or as text when it is not JSON
  • headers, with names in lower case
  • query, the query string’s values by name
  • path, the part of the URL after the source’s address

A plain value matches an equal value, and an object matches field by field, so the fields you leave out can hold anything. For other tests, use an object of operators in place of a value: $eq, $neq, $in, $nin, $gt, $gte, $lt, $lte, $exists, $startsWith, $endsWith, and $contains. $and, $or, and $not combine conditions. One object holds either operators or field names, not both.

{
  "headers": { "x-provider": { "$in": ["mpesa", "airtel-money"] } },
  "body": {
    "status": "COMPLETED",
    "amount": { "$gte": 10000 },
    "msisdn": { "$startsWith": "25574" }
  }
}

A filter is at most 16 KB and 16 levels deep, and it is checked when the connection is saved. No filter, or null, takes every webhook.

Transformations

A transformation is JavaScript that changes a webhook before it is sent. It defines a function transform(request), which may be async, and returns the headers and body to send:

function transform(request) {
  const payment = request.body
  return {
    headers: { "x-order-id": payment.reference },
    body: {
      orderId: payment.reference,
      amountTzs: payment.amount,
      phone: payment.msisdn,
      paidAt: payment.timestamp,
    },
  }
}

request holds method, path, query as a string, headers with names in lower case, and body: the parsed JSON when it is JSON, text otherwise, or base64 with bodyEncoding: "base64" when it is neither.

The object it returns replaces the webhook’s headers and body. headers is an object of at most 100 names and values. body is sent as JSON when it is an object or an array, with Content-Type: application/json unless you set one; as text when it is a string; and as bytes when it is a base64 string and you also return bodyEncoding: "base64". The destination’s authentication and signing headers are added as usual.

  • When it runs. Filters see the webhook as it was received. The transformation runs on a delivery’s first attempt, and what it returns is kept, so every retry sends the same request. Sending a delivery again runs the connection’s current transformation, so a fixed transformation applies to failed webhooks you send again.
  • What it can do. It runs for at most 100 ms with at most 128 MB of memory. It cannot fetch, read files, use timers, or import modules. console.log lines are kept for space.hooks.transformations.validate().
  • When it fails. A transformation that throws, returns the wrong shape, or runs out of time or memory fails its delivery at once, with the reason in lastError. It does not count against the destination.

The code is at most 64 KB. Saving a connection refuses code that does not parse or defines no transform.

space.hooks.connections.create()

Connects a source to a destination.

space.hooks.connections.create(
  connection: ConnectionInput,
  options?: {
    signal?: AbortSignal
    timeoutMs?: number
  }
): Promise<Connection>
connection.sourceIdstringRequired

The ID of a source in the project, 26 lowercase letters and digits.

connection.destinationIdstringRequired

The ID of a destination in the project.

connection.statestring

enabled or paused. enabled when you leave it out. A paused connection makes no deliveries, and webhooks that arrive while it is paused are not sent through it later.

connection.filterRecord<string, unknown> | null

Which webhooks to take. See Filters. Every webhook when you leave it out or pass null.

connection.delaySecondsinteger

How long to wait before the first attempt, 0 to 86,400 seconds. 0 when you leave it out.

connection.transformationstring | null

JavaScript that defines transform(request), at most 64 KB. See Transformations. None when you leave it out or pass null.

options.signalAbortSignal

Cancels the call when it aborts. The promise then rejects with aborted.

options.timeoutMsnumber

The longest each request may take, in milliseconds. The client’s timeoutMs when you leave it out, 30,000 by default.

Returns a promise of the new connection. create() is not sent again after a network failure or a timeout, because the connection may already exist.

CodeWhen
validation_failedA field breaks a rule above or is not listed, the source or destination is not in the project, the filter cannot be read, or the transformation cannot run. issues names each field
hooks_limit_reachedThe project already holds 200 connections
create-connection.ts
import { space } from "digitospace"

const connection = await space.hooks.connections.create({
  sourceId: "01m3m5gpkrvj4qdxwkde3vt4w9",
  destinationId: "01m3m5gpbttxs7xnhk0jgyt4wa",
  filter: {
    body: { status: "COMPLETED", amount: { $gte: 1000 } },
  },
  delaySeconds: 5,
})

console.log(connection.id)

space.hooks.connections.get()

Reads one connection.

space.hooks.connections.get(
  connection: string,
  options?: {
    signal?: AbortSignal
    timeoutMs?: number
  }
): Promise<Connection>
connectionstringRequired

The connection’s ID, 26 lowercase letters and digits.

options.signalAbortSignal

Cancels the call when it aborts.

options.timeoutMsnumber

The longest each request may take, in milliseconds.

Returns a promise of the connection.

CodeWhen
validation_failedThe ID is not 26 lowercase letters and digits. Nothing is sent
not_foundThe project has no connection with that ID
get-connection.ts
import { space } from "digitospace"

const connection = await space.hooks.connections.get("01m3m5hezf7epksb7f8qbww4wb")

console.log(connection.filter, connection.transformation !== null)

space.hooks.connections.list()

Lists the project’s connections, newest first: all of them, or those of one source or destination.

space.hooks.connections.list(filter?: {
  sourceId?: string
  destinationId?: string
  signal?: AbortSignal
  timeoutMs?: number
}): Paged<Connection>
filter.sourceIdstring

Only the connections from this source.

filter.destinationIdstring

Only the connections to this destination.

filter.signalAbortSignal

Cancels the call when it aborts.

filter.timeoutMsnumber

The longest each request may take, in milliseconds.

Returns a Paged<Connection> at once, without a request. It reads the connections 200 at a time as you go: loop over it with for await, collect every connection with .all(), or read one page with .page(cursor), which resolves to { items, nextCursor }.

The IDs are checked when the list is read, not when you call list(). A wrong one rejects the first read with validation_failed, and nothing is sent.

list-connections.ts
import { space } from "digitospace"

const connections = await space.hooks.connections
  .list({ sourceId: "01m3m5gpkrvj4qdxwkde3vt4w9" })
  .all()

console.log(`${connections.length} destinations take these payments`)

space.hooks.connections.update()

Changes some of a connection’s fields and keeps the rest. It reads the connection first, then saves it whole with your changes, so two updates at the same time can undo each other.

space.hooks.connections.update(
  connection: string,
  change: Partial<ConnectionInput>,
  options?: {
    signal?: AbortSignal
    timeoutMs?: number
  }
): Promise<Connection>
connectionstringRequired

The connection’s ID.

changePartial<ConnectionInput>Required

The fields to change, with the same rules as create(). A field you leave out keeps its value. filter: null removes the filter, and transformation: null removes the transformation.

options.signalAbortSignal

Cancels the call when it aborts.

options.timeoutMsnumber

The longest each request may take, in milliseconds.

Returns a promise of the changed connection. A new filter or delay applies to webhooks that arrive afterwards. A new transformation applies to every delivery that has not had its first attempt yet, and to deliveries sent again.

CodeWhen
validation_failedA field breaks a rule, the source or destination is not in the project, the filter cannot be read, or the transformation cannot run. issues names each field
not_foundThe project has no connection with that ID
update-connection.ts
import { space } from "digitospace"

await space.hooks.connections.update("01m3m5hezf7epksb7f8qbww4wb", {
  transformation: `function transform(request) {
  return {
    headers: {},
    body: { orderId: request.body.reference, amountTzs: request.body.amount },
  }
}`,
})

await space.hooks.connections.update("01m3m5hezf7epksb7f8qbww4wb", {
  transformation: null,
})

space.hooks.connections.pause()

Pauses a connection. It makes no deliveries while it is paused, and webhooks that arrive in the meantime are not sent through it later. Deliveries it already made carry on.

space.hooks.connections.pause(
  connection: string,
  options?: {
    signal?: AbortSignal
    timeoutMs?: number
  }
): Promise<Connection>
connectionstringRequired

The connection’s ID.

options.signalAbortSignal

Cancels the call when it aborts.

options.timeoutMsnumber

The longest each request may take, in milliseconds.

The same as update(connection, { state: "paused" }), with the same errors. Returns a promise of the paused connection.

pause-connection.ts
import { space } from "digitospace"

await space.hooks.connections.pause("01m3m5hezf7epksb7f8qbww4wb")

space.hooks.connections.resume()

Enables a paused connection. Webhooks that arrive from then on are sent through it.

space.hooks.connections.resume(
  connection: string,
  options?: {
    signal?: AbortSignal
    timeoutMs?: number
  }
): Promise<Connection>
connectionstringRequired

The connection’s ID.

options.signalAbortSignal

Cancels the call when it aborts.

options.timeoutMsnumber

The longest each request may take, in milliseconds.

The same as update(connection, { state: "enabled" }), with the same errors. Returns a promise of the enabled connection.

resume-connection.ts
import { space } from "digitospace"

await space.hooks.connections.resume("01m3m5hezf7epksb7f8qbww4wb")

space.hooks.connections.delete()

Deletes a connection. Its source and destination stay, and deliveries it already made carry on, with connectionId set to null. Those that have not had their first attempt yet are sent without its transformation. This cannot be undone.

space.hooks.connections.delete(
  connection: string,
  options?: {
    signal?: AbortSignal
    timeoutMs?: number
  }
): Promise<void>
connectionstringRequired

The connection’s ID.

options.signalAbortSignal

Cancels the call when it aborts.

options.timeoutMsnumber

The longest each request may take, in milliseconds.

Returns a promise that resolves when the connection is gone.

CodeWhen
validation_failedThe ID is not 26 lowercase letters and digits. Nothing is sent
not_foundThe project has no connection with that ID
delete-connection.ts
import { space } from "digitospace"

await space.hooks.connections.delete("01m3m5hezf7epksb7f8qbww4wb")

Last updated