TypeScript client
A fully typed client for the whole API, generated from the same OpenAPI document the reference is built from. Copy it into your project — every endpoint is a method, every request and response is typed, and it cannot drift from the API because it is regenerated from the spec.
There is no published npm package yet
We would rather give you a file that works today than an install command that 404s. This client is complete and supported; when a package ships, it is generated from exactly this.
Using it
- 1. Save the file below as
ryabi.tsin your project. - 2. Put your API key in an environment variable. Never in the code — that is how keys end up in public repositories.
- 3. Start with a test key. The client exposes
isTestModeso you can assert it in your own tests.
import { RyabiClient, RyabiApiError } from "./ryabi";
const ryabi = new RyabiClient({ apiKey: process.env.RYABI_API_KEY! });
try {
const link = await ryabi.createALink({
destinationUrl: "https://example.com/spring-sale",
slug: "spring-sale",
});
console.log(link.shortUrl);
} catch (err) {
if (err instanceof RyabiApiError) {
// Codes are stable; messages are written for people and can change.
console.error(err.code, err.message);
if (err.retryable) {
// 429 and 5xx only. Retrying any other 4xx cannot succeed.
}
}
}Other languages
The OpenAPI document works with the standard generators, so a client in any language is one command away:
# Python, Go, Java, Ruby, PHP, C#, Rust and about forty more
npx @openapitools/openapi-generator-cli generate \
-i https://www.ryabils.com/api/v1/openapi.json \
-g python \
-o ./ryabi-clientEvery endpoint in the reference is in that document, so a generated client covers the whole API rather than the parts somebody remembered to write up.
The client
ryabi.ts · 588 lines
/**
* Ryabi Links — a typed client for the REST API.
*
* Generated from the OpenAPI document. Copy this file into your project; there
* is nothing to install and nothing to keep in step by hand.
*
* Usage:
*
* const ryabi = new RyabiClient({ apiKey: process.env.RYABI_API_KEY! });
* const link = await ryabi.createALink({
* destinationUrl: "https://example.com/landing",
* });
* console.log(link.shortUrl);
*
* Every method throws RyabiApiError on a non-2xx response, carrying the code
* from the error envelope. Codes are documented at /docs/api/errors — treat a
* 4xx as fatal and a 429 or 5xx as worth retrying, with a backoff.
*/
export interface RyabiClientOptions {
/** Your API key. Starts lnk_live_ or lnk_test_. Never hard-code it. */
apiKey: string;
/** Override the API root. You will not normally need this. */
baseUrl?: string;
/** Pin a dated API version. Leave unset to track the current one. */
apiVersion?: string;
/** Act in a specific workspace. Defaults to the key's own workspace. */
workspaceId?: string;
/** Supply your own fetch (a test double, or an instrumented one). */
fetch?: typeof globalThis.fetch;
}
/** Thrown for any non-2xx response. */
export class RyabiApiError extends Error {
readonly status: number;
readonly code: string;
readonly details?: Record<string, unknown>;
constructor(
status: number,
code: string,
message: string,
details?: Record<string, unknown>
) {
super(message);
this.name = "RyabiApiError";
this.status = status;
this.code = code;
this.details = details;
}
/**
* Retrying a 4xx will never succeed — the request is wrong, and sending it
* again just spends your rate limit. 429 is the exception: it means slow
* down, not stop.
*/
get retryable(): boolean {
return this.status === 429 || this.status >= 500;
}
}
interface Envelope<T> {
success: boolean;
data?: T;
error?: { code: string; message: string; details?: Record<string, unknown> };
meta?: { pagination?: { page: number; perPage: number; total: number; totalPages: number } };
}
const DEFAULT_BASE_URL = "https://www.ryabils.com/api/v1";
export class RyabiClient {
private readonly apiKey: string;
private readonly baseUrl: string;
private readonly apiVersion?: string;
private readonly workspaceId?: string;
private readonly fetchImpl: typeof globalThis.fetch;
constructor(options: RyabiClientOptions) {
if (!options.apiKey) {
throw new Error("RyabiClient: apiKey is required");
}
this.apiKey = options.apiKey;
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
this.apiVersion = options.apiVersion;
this.workspaceId = options.workspaceId;
this.fetchImpl = options.fetch ?? globalThis.fetch;
}
/** True when this client is using a sandboxed test key. */
get isTestMode(): boolean {
return this.apiKey.startsWith("lnk_test_");
}
private async request<T>(
method: string,
path: string,
options: { query?: Record<string, unknown>; body?: unknown } = {}
): Promise<T> {
const url = new URL(this.baseUrl + path);
for (const [key, value] of Object.entries(options.query ?? {})) {
if (value === undefined || value === null) continue;
url.searchParams.set(key, String(value));
}
const headers: Record<string, string> = {
Authorization: `Bearer ${this.apiKey}`,
};
if (options.body !== undefined) headers["Content-Type"] = "application/json";
if (this.apiVersion) headers["Ryabi-Version"] = this.apiVersion;
if (this.workspaceId) headers["X-Workspace-Id"] = this.workspaceId;
const response = await this.fetchImpl(url.toString(), {
method,
headers,
body: options.body === undefined ? undefined : JSON.stringify(options.body),
});
let payload: Envelope<T>;
try {
payload = (await response.json()) as Envelope<T>;
} catch {
// A body we cannot parse is still a failure worth naming precisely —
// "unexpected end of JSON input" tells the caller nothing about what
// actually went wrong.
throw new RyabiApiError(
response.status,
"INVALID_RESPONSE",
`The API returned a ${response.status} with a body that was not JSON.`
);
}
if (!response.ok || !payload.success) {
throw new RyabiApiError(
response.status,
payload.error?.code ?? "UNKNOWN",
payload.error?.message ?? `Request failed with ${response.status}`,
payload.error?.details
);
}
return payload.data as T;
}
/**
* Identity / auth test
*
* Returns the authenticated user + workspace. Used by integrations (Zapier/Make/n8n) to validate an API key.
*
* `GET /me`
*/
identityAuthTest(): Promise<unknown> {
return this.request<unknown>("GET", `/me`, {
});
}
/**
* List links
*
* `GET /links`
*/
listLinks(query?: { page?: number; perPage?: number; search?: string; include?: "qr" }): Promise<{ id?: string; slug?: string; domain?: string; destinationUrl?: string; shortUrl?: string; title?: string | null; description?: string | null; tags?: string[]; totalClicks?: number; uniqueClicks?: number; isActive?: boolean; isArchived?: boolean; isTest?: boolean; expiresAt?: string | null; createdAt?: string; qr?: { svg?: string; png?: string } }[]> {
return this.request<{ id?: string; slug?: string; domain?: string; destinationUrl?: string; shortUrl?: string; title?: string | null; description?: string | null; tags?: string[]; totalClicks?: number; uniqueClicks?: number; isActive?: boolean; isArchived?: boolean; isTest?: boolean; expiresAt?: string | null; createdAt?: string; qr?: { svg?: string; png?: string } }[]>("GET", `/links`, {
query,
});
}
/**
* Create a link
*
* Shorten a URL. Only destinationUrl is required.
*
* Leave slug out and we generate one that is not already taken. Leave domain out and we pick from the shared pool; pass one of your own verified domains to use it instead.
*
* A key created in test mode produces a sandboxed link: it comes back exactly like any other, but it never redirects, so it records no clicks and counts against neither your plan allowance nor your billing.
*
* Send `"include": ["qr"]` to get the link's QR code back with it — inline SVG plus a PNG URL — turning one call into a poster or an email. Off by default.
*
* `POST /links`
*/
createALink(body: { include?: "qr"[]; destinationUrl: string; slug?: string; domain?: string; title?: string | null; description?: string | null; tags?: string[]; folderId?: string | null; campaignId?: string | null; redirectStatus?: 301 | 302 | 307 | 308; fallbackUrl?: string | null; password?: string | null; expiresAt?: string | null; clickLimit?: number | null; uniqueIpLimit?: number | null; publicStatsEnabled?: boolean; utmSource?: string | null; utmMedium?: string | null; utmCampaign?: string | null; utmTerm?: string | null; utmContent?: string | null; ogTitle?: string | null; ogDescription?: string | null; ogImageUrl?: string | null; twitterCard?: "summary" | "summary_large_image" | "player"; deepLinkEnabled?: boolean; captchaEnabled?: boolean; consentRequired?: boolean }): Promise<{ id?: string; slug?: string; domain?: string; destinationUrl?: string; shortUrl?: string; title?: string | null; description?: string | null; tags?: string[]; totalClicks?: number; uniqueClicks?: number; isActive?: boolean; isArchived?: boolean; isTest?: boolean; expiresAt?: string | null; createdAt?: string; qr?: { svg?: string; png?: string } }> {
return this.request<{ id?: string; slug?: string; domain?: string; destinationUrl?: string; shortUrl?: string; title?: string | null; description?: string | null; tags?: string[]; totalClicks?: number; uniqueClicks?: number; isActive?: boolean; isArchived?: boolean; isTest?: boolean; expiresAt?: string | null; createdAt?: string; qr?: { svg?: string; png?: string } }>("POST", `/links`, {
body,
});
}
/**
* Bulk-create links
*
* `POST /links/bulk`
*/
bulkCreateLinks(body: { links: { include?: "qr"[]; destinationUrl: string; slug?: string; domain?: string; title?: string | null; description?: string | null; tags?: string[]; folderId?: string | null; campaignId?: string | null; redirectStatus?: 301 | 302 | 307 | 308; fallbackUrl?: string | null; password?: string | null; expiresAt?: string | null; clickLimit?: number | null; uniqueIpLimit?: number | null; publicStatsEnabled?: boolean; utmSource?: string | null; utmMedium?: string | null; utmCampaign?: string | null; utmTerm?: string | null; utmContent?: string | null; ogTitle?: string | null; ogDescription?: string | null; ogImageUrl?: string | null; twitterCard?: "summary" | "summary_large_image" | "player"; deepLinkEnabled?: boolean; captchaEnabled?: boolean; consentRequired?: boolean }[] }): Promise<unknown> {
return this.request<unknown>("POST", `/links/bulk`, {
body,
});
}
/**
* Get a link
*
* `GET /links/{id}`
*/
getALink(id: string): Promise<{ id?: string; slug?: string; domain?: string; destinationUrl?: string; shortUrl?: string; title?: string | null; description?: string | null; tags?: string[]; totalClicks?: number; uniqueClicks?: number; isActive?: boolean; isArchived?: boolean; isTest?: boolean; expiresAt?: string | null; createdAt?: string; qr?: { svg?: string; png?: string } }> {
return this.request<{ id?: string; slug?: string; domain?: string; destinationUrl?: string; shortUrl?: string; title?: string | null; description?: string | null; tags?: string[]; totalClicks?: number; uniqueClicks?: number; isActive?: boolean; isArchived?: boolean; isTest?: boolean; expiresAt?: string | null; createdAt?: string; qr?: { svg?: string; png?: string } }>("GET", `/links/${encodeURIComponent(id)}`, {
});
}
/**
* Update a link
*
* `PATCH /links/{id}`
*/
updateALink(id: string, body?: { include?: "qr"[]; destinationUrl: string; slug?: string; domain?: string; title?: string | null; description?: string | null; tags?: string[]; folderId?: string | null; campaignId?: string | null; redirectStatus?: 301 | 302 | 307 | 308; fallbackUrl?: string | null; password?: string | null; expiresAt?: string | null; clickLimit?: number | null; uniqueIpLimit?: number | null; publicStatsEnabled?: boolean; utmSource?: string | null; utmMedium?: string | null; utmCampaign?: string | null; utmTerm?: string | null; utmContent?: string | null; ogTitle?: string | null; ogDescription?: string | null; ogImageUrl?: string | null; twitterCard?: "summary" | "summary_large_image" | "player"; deepLinkEnabled?: boolean; captchaEnabled?: boolean; consentRequired?: boolean }): Promise<unknown> {
return this.request<unknown>("PATCH", `/links/${encodeURIComponent(id)}`, {
body,
});
}
/**
* Delete a link
*
* `DELETE /links/{id}`
*/
deleteALink(id: string): Promise<unknown> {
return this.request<unknown>("DELETE", `/links/${encodeURIComponent(id)}`, {
});
}
/**
* Workspace analytics overview
*
* `GET /analytics/overview`
*/
workspaceAnalyticsOverview(): Promise<unknown> {
return this.request<unknown>("GET", `/analytics/overview`, {
});
}
/**
* Clicks over time
*
* `GET /analytics/timeline`
*/
clicksOverTime(query?: { linkId?: string; from?: string; to?: string }): Promise<unknown> {
return this.request<unknown>("GET", `/analytics/timeline`, {
query,
});
}
/**
* Top links by clicks
*
* `GET /analytics/top-links`
*/
topLinksByClicks(): Promise<unknown> {
return this.request<unknown>("GET", `/analytics/top-links`, {
});
}
/**
* Generate a QR code (PNG/SVG/PDF)
*
* `GET /qr/{linkId}`
*/
generateAQrCodePngSvgPdf(linkId: string, query?: { format?: "png" | "svg" | "pdf"; size?: number }): Promise<unknown> {
return this.request<unknown>("GET", `/qr/${encodeURIComponent(linkId)}`, {
query,
});
}
/**
* List custom domains
*
* `GET /domains`
*/
listCustomDomains(): Promise<{ id?: string; domain?: string; verificationStatus?: string; sslStatus?: string }[]> {
return this.request<{ id?: string; domain?: string; verificationStatus?: string; sslStatus?: string }[]>("GET", `/domains`, {
});
}
/**
* List webhook endpoints
*
* `GET /webhooks`
*/
listWebhookEndpoints(): Promise<{ id?: string; name?: string | null; url?: string; events?: string[]; format?: "standard" | "slack" | "discord"; mode?: "live" | "test"; isActive?: boolean; failureCount?: number; disabledReason?: string | null; customHeaderNames?: { name?: string; hint?: string }[]; filters?: { match?: "all" | "any"; events?: string[] | null; rules?: { field?: string; op?: "eq" | "neq" | "in" | "nin" | "contains" | "not_contains" | "starts_with" | "gt" | "gte" | "lt" | "lte" | "exists" | "not_exists"; value?: unknown }[] } | null; transform?: { kind?: "template"; template?: Record<string, unknown> } | null; batchEnabled?: boolean; batchWindowSeconds?: number; maxConcurrent?: number; mtlsEnabled?: boolean; previousSecretExpiresAt?: string | null }[]> {
return this.request<{ id?: string; name?: string | null; url?: string; events?: string[]; format?: "standard" | "slack" | "discord"; mode?: "live" | "test"; isActive?: boolean; failureCount?: number; disabledReason?: string | null; customHeaderNames?: { name?: string; hint?: string }[]; filters?: { match?: "all" | "any"; events?: string[] | null; rules?: { field?: string; op?: "eq" | "neq" | "in" | "nin" | "contains" | "not_contains" | "starts_with" | "gt" | "gte" | "lt" | "lte" | "exists" | "not_exists"; value?: unknown }[] } | null; transform?: { kind?: "template"; template?: Record<string, unknown> } | null; batchEnabled?: boolean; batchWindowSeconds?: number; maxConcurrent?: number; mtlsEnabled?: boolean; previousSecretExpiresAt?: string | null }[]>("GET", `/webhooks`, {
});
}
/**
* List deliveries, with filtering, search and a live cursor
*
* Pass `since` (an ISO timestamp) to poll for anything newer — that is how the dashboard's live log works. `status=failed` covers both a delivery still retrying and one that gave up.
*
* `GET /webhooks/{id}/deliveries`
*/
listDeliveriesWithFilteringSearchAndALiveCursor(id: string, query?: { status?: "all" | "pending" | "success" | "failed" | "dead"; event?: string; from?: string; to?: string; q?: string; since?: string; limit?: number }): Promise<unknown> {
return this.request<unknown>("GET", `/webhooks/${encodeURIComponent(id)}/deliveries`, {
query,
});
}
/**
* One delivery, with the exact request and response
*
* Request headers come back with credentials and signatures redacted to a hint — the value never leaves the server.
*
* `GET /webhooks/{id}/deliveries/{deliveryId}`
*/
oneDeliveryWithTheExactRequestAndResponse(id: string, deliveryId: string): Promise<{ id?: string; event?: string; status?: "pending" | "success" | "failed" | "dead"; responseStatus?: number | null; duration?: number | null; attempt?: number; maxAttempts?: number; batchSize?: number; nextAttemptAt?: string | null; deliveredAt?: string; errorMessage?: string | null; replayOfId?: string | null; replayable?: boolean }> {
return this.request<{ id?: string; event?: string; status?: "pending" | "success" | "failed" | "dead"; responseStatus?: number | null; duration?: number | null; attempt?: number; maxAttempts?: number; batchSize?: number; nextAttemptAt?: string | null; deliveredAt?: string; errorMessage?: string | null; replayOfId?: string | null; replayable?: boolean }>("GET", `/webhooks/${encodeURIComponent(id)}/deliveries/${encodeURIComponent(deliveryId)}`, {
});
}
/**
* Send one delivery again
*
* Creates a NEW delivery with a NEW idempotency key, never a further attempt at the old one — so a receiver de-duplicating on the key does not silently ignore it. Refused with 409 while the endpoint is paused.
*
* `POST /webhooks/{id}/deliveries/{deliveryId}/replay`
*/
sendOneDeliveryAgain(id: string, deliveryId: string): Promise<unknown> {
return this.request<unknown>("POST", `/webhooks/${encodeURIComponent(id)}/deliveries/${encodeURIComponent(deliveryId)}/replay`, {
});
}
/**
* Re-queue the dead letter queue
*
* Queues up to 200 dead deliveries, oldest first, and answers with how many are left. Capped on purpose: firing thousands at a server that has only just come back up is the thundering herd the concurrency limit exists to prevent.
*
* `POST /webhooks/{id}/replay-all`
*/
reQueueTheDeadLetterQueue(id: string): Promise<unknown> {
return this.request<unknown>("POST", `/webhooks/${encodeURIComponent(id)}/replay-all`, {
});
}
/**
* New signing secret
*
* By default the old secret keeps working for 24 hours and both signatures ride in `x-ryabi-signature`, so a receiver can be updated without dropping events. Send `{ "grace": false }` when the secret LEAKED — then the old one stops immediately. The secret is returned once and never again.
*
* `POST /webhooks/{id}/rotate-secret`
*/
newSigningSecret(id: string): Promise<unknown> {
return this.request<unknown>("POST", `/webhooks/${encodeURIComponent(id)}/rotate-secret`, {
});
}
/**
* Send a test ping
*
* Goes through the same sender as a real delivery, so a green test proves the signature, the custom headers and the mutual TLS handshake your real events will use. Ignores the endpoint's filter, template and batching — you are testing the connection, not the routing. Never leaves a retry scheduled.
*
* `POST /webhooks/{id}/test`
*/
sendATestPing(id: string): Promise<unknown> {
return this.request<unknown>("POST", `/webhooks/${encodeURIComponent(id)}/test`, {
});
}
/**
* Delivery facts — outbound IPs, retry schedule, timeout
*
* `egressIpsConfigured` is false until an operator pins static outbound addresses. Do not write a firewall rule against an empty list.
*
* `GET /webhooks/info`
*/
deliveryFactsOutboundIpsRetryScheduleTimeout(): Promise<unknown> {
return this.request<unknown>("GET", `/webhooks/info`, {
});
}
/**
* Download the event payload types
*
* TypeScript interfaces or Go structs, generated from the live event catalogue so they cannot be stale.
*
* `GET /webhooks/types`
*/
downloadTheEventPayloadTypes(query?: { lang?: "ts" | "go" }): Promise<unknown> {
return this.request<unknown>("GET", `/webhooks/types`, {
query,
});
}
/**
* List open forwarding sessions
*
* `GET /webhooks/cli/sessions`
*/
listOpenForwardingSessions(): Promise<unknown> {
return this.request<unknown>("GET", `/webhooks/cli/sessions`, {
});
}
/**
* Open a localhost forwarding session
*
* What `ryabi listen` calls. Returns a session id and a signing secret. Every connection is outbound from your machine, so there is no tunnel and no port to open.
*
* `POST /webhooks/cli/sessions`
*/
openALocalhostForwardingSession(): Promise<unknown> {
return this.request<unknown>("POST", `/webhooks/cli/sessions`, {
});
}
/**
* Poll a forwarding session for events
*
* Waits a few seconds for the first event, then answers. Events are removed as they are handed over, so a second poll never repeats them. The poll IS the heartbeat — stop polling and the session expires.
*
* `GET /webhooks/cli/events`
*/
pollAForwardingSessionForEvents(query?: { session?: string }): Promise<unknown> {
return this.request<unknown>("GET", `/webhooks/cli/events`, {
query,
});
}
/**
* List API keys
*
* `GET /api-keys`
*/
listApiKeys(): Promise<{ id?: string; name?: string; keyPrefix?: string; mode?: "live" | "test"; permissions?: string[]; allowedIps?: string[]; scopeFilters?: { domains?: string[]; tags?: string[] } | null; schedule?: { timezone?: string; days?: number[]; startMinute?: number; endMinute?: number } | null; monthlyCallCap?: number | null; rateLimitOverride?: number | null; expiresAt?: string | null; lastUsedAt?: string | null; lastUsedIp?: string | null; pausedAt?: string | null; pausedReason?: string | null; revokedAt?: string | null; revokedReason?: string | null; rotating?: boolean; graceHoursRemaining?: number; createdAt?: string }[]> {
return this.request<{ id?: string; name?: string; keyPrefix?: string; mode?: "live" | "test"; permissions?: string[]; allowedIps?: string[]; scopeFilters?: { domains?: string[]; tags?: string[] } | null; schedule?: { timezone?: string; days?: number[]; startMinute?: number; endMinute?: number } | null; monthlyCallCap?: number | null; rateLimitOverride?: number | null; expiresAt?: string | null; lastUsedAt?: string | null; lastUsedIp?: string | null; pausedAt?: string | null; pausedReason?: string | null; revokedAt?: string | null; revokedReason?: string | null; rotating?: boolean; graceHoursRemaining?: number; createdAt?: string }[]>("GET", `/api-keys`, {
});
}
/**
* List templates (public, no auth)
*
* `GET /templates`
*/
listTemplatesPublicNoAuth(query?: { category?: string; tag?: string; q?: string; limit?: number; offset?: number }): Promise<unknown> {
return this.request<unknown>("GET", `/templates`, {
query,
});
}
/**
* One template, including the page tree it builds (public, no auth)
*
* `GET /templates/{slug}`
*/
oneTemplateIncludingThePageTreeItBuildsPublicNoAuth(slug: string): Promise<unknown> {
return this.request<unknown>("GET", `/templates/${encodeURIComponent(slug)}`, {
});
}
/**
* List bridge pages
*
* `GET /bridge-pages`
*/
listBridgePages(): Promise<unknown> {
return this.request<unknown>("GET", `/bridge-pages`, {
});
}
/**
* Create a page (optionally from a template)
*
* `POST /bridge-pages`
*/
createAPageOptionallyFromATemplate(body?: { title?: string; slug?: string; templateSlug?: string; savedTemplateId?: string }): Promise<unknown> {
return this.request<unknown>("POST", `/bridge-pages`, {
body,
});
}
/**
* List a page's blocks, flat and in reading order
*
* `GET /bridge-pages/{pageId}/blocks`
*/
listAPageSBlocksFlatAndInReadingOrder(pageId: string): Promise<unknown> {
return this.request<unknown>("GET", `/bridge-pages/${encodeURIComponent(pageId)}/blocks`, {
});
}
/**
* Append a block
*
* Starts from the block type's defaults, then overlays `fields`. `id` and `type` inside `fields` are ignored. Layout is preserved; an empty page gets one full-width section.
*
* `POST /bridge-pages/{pageId}/blocks`
*/
appendABlock(pageId: string, body?: { type: string; fields?: Record<string, unknown> }): Promise<unknown> {
return this.request<unknown>("POST", `/bridge-pages/${encodeURIComponent(pageId)}/blocks`, {
body,
});
}
/**
* Read one block
*
* `GET /bridge-pages/{pageId}/blocks/{blockId}`
*/
readOneBlock(pageId: string, blockId: string): Promise<unknown> {
return this.request<unknown>("GET", `/bridge-pages/${encodeURIComponent(pageId)}/blocks/${encodeURIComponent(blockId)}`, {
});
}
/**
* Update one block's fields
*
* Merges the body into the block. Changing `type` is refused with a 400.
*
* `PATCH /bridge-pages/{pageId}/blocks/{blockId}`
*/
updateOneBlockSFields(pageId: string, blockId: string, body?: Record<string, unknown>): Promise<unknown> {
return this.request<unknown>("PATCH", `/bridge-pages/${encodeURIComponent(pageId)}/blocks/${encodeURIComponent(blockId)}`, {
body,
});
}
/**
* Delete one block
*
* `DELETE /bridge-pages/{pageId}/blocks/{blockId}`
*/
deleteOneBlock(pageId: string, blockId: string): Promise<unknown> {
return this.request<unknown>("DELETE", `/bridge-pages/${encodeURIComponent(pageId)}/blocks/${encodeURIComponent(blockId)}`, {
});
}
/**
* List automation subscriptions
*
* `GET /zapier/subscriptions`
*/
listAutomationSubscriptions(): Promise<unknown> {
return this.request<unknown>("GET", `/zapier/subscriptions`, {
});
}
/**
* Subscribe a Zap / scenario to an event (REST hook)
*
* Called when an automation is switched ON. Accepts `hookUrl` (Zapier) or `targetUrl` (Make); https only. Deliveries reuse the signed, retried webhook pipeline.
*
* `POST /zapier/subscriptions`
*/
subscribeAZapScenarioToAnEventRestHook(body?: { event: string; hookUrl?: string; targetUrl?: string }): Promise<unknown> {
return this.request<unknown>("POST", `/zapier/subscriptions`, {
body,
});
}
/**
* Unsubscribe (idempotent)
*
* `DELETE /zapier/subscriptions/{id}`
*/
unsubscribeIdempotent(id: string): Promise<unknown> {
return this.request<unknown>("DELETE", `/zapier/subscriptions/${encodeURIComponent(id)}`, {
});
}
/**
* Sample event data for an automation's field mapper
*
* Returns your most recent real delivery of the event when there is one, else a synthetic example flagged `sample: true`. Omit `event` for the whole catalogue.
*
* `GET /zapier/samples`
*/
sampleEventDataForAnAutomationSFieldMapper(query?: { event?: string; flat?: "1" }): Promise<unknown> {
return this.request<unknown>("GET", `/zapier/samples`, {
query,
});
}
/**
* Record a conversion (public beacon, no auth)
*
* `POST /conversions`
*/
recordAConversionPublicBeaconNoAuth(body?: { clickId?: string; eventName?: string; value?: number; currency?: string; metadata?: Record<string, unknown> }): Promise<unknown> {
return this.request<unknown>("POST", `/conversions`, {
body,
});
}
}