slsh.me

api.slsh.me › back to slsh.me

slsh.me API v1

Bearer-token auth, JSON in/out, real-time clicks. Base URL: https://api.slsh.me/v1.

Authentication

Every request to /v1/* must include an Authorization: Bearer <token> header. Create tokens at app.slsh.me/settings/api. Tokens are named PATs; revoke any of them individually.

Missing or invalid tokens return 401 {"error": "invalid_token"}.

Pagination

List endpoints (GET /v1/links, GET /v1/links/:slug/clicks) return at most 100 items per page. Pass ?page=N (1-indexed; default 1) to navigate.

NameDescription
X-Total-Count Total items across all pages.
Link RFC 5988 link header with pre-built URLs for next, prev, first, and last — parse this rather than computing page numbers yourself.

Example. When you ask for page 1 of a 250-item resource, the server replies with:

X-Total-Count: 250
Link: <https://api.slsh.me/v1/links?page=2>; rel="next", <https://api.slsh.me/v1/links?page=1>; rel="first", <https://api.slsh.me/v1/links?page=3>; rel="last"

Walk forward by following rel="next" until it's no longer present.

List your campaigns

GET /v1/campaigns

Returns the authenticated organisation's campaigns, newest first. Archived campaigns are hidden; pass ?archived=true to list those instead. Paginated with ?page=N (1-indexed, 100/page); the response carries X-Total-Count and a standard Link header (RFC 5988).

200 Success
401 Missing or invalid token

Create a campaign

POST /v1/campaigns

The slug is generated from the name when omitted, and becomes the default utm_campaign every link in the campaign inherits. A campaign's default custom domain is set in the web app — this endpoint always creates a campaign on the default domain.

NameTypeRequiredDescription
name String required Display name
slug String Lowercase slug, unique within your organisation
color String One of orange, blue, emerald, violet, rose
icon String Phosphor icon name (fill weight)
destination_url String Shared destination for the campaign's channel links
title String Open Graph title
description String Open Graph description
goal_clicks Integer Click target for the campaign report
starts_at String ISO 8601 start timestamp
ends_at String ISO 8601 end timestamp; a campaign past it stops counting against your plan cap
utm_source String utm_source every link in the campaign inherits
utm_medium String utm_medium every link in the campaign inherits
utm_campaign String Overrides the slug as the inherited utm_campaign
utm_term String utm_term every link in the campaign inherits
utm_content String utm_content every link in the campaign inherits
custom_utms Hash Arbitrary UTM defaults keyed by bare suffix, e.g. {"office": "acme"}
201 Created
401 Missing or invalid token
422 Invalid params, or your plan's active-campaign cap is reached

Fetch a campaign by id

GET /v1/campaigns/:id

clicks_count is the sum across every link in the campaign, links_count the number of links filed under it.

200 Success
401 Missing or invalid token
404 Unknown campaign, or it belongs to another organisation

Update a campaign

PATCH /v1/campaigns/:id

Every create field can be changed. Changing the UTM preset re-composes the destination of every link in the campaign that hasn't overridden the key being changed. Pass archived: false to restore an archived campaign.

NameTypeRequiredDescription
name String Display name
slug String Lowercase slug, unique within your organisation
color String One of orange, blue, emerald, violet, rose
icon String Phosphor icon name (fill weight)
destination_url String Shared destination for the campaign's channel links
title String Open Graph title
description String Open Graph description
goal_clicks Integer Click target for the campaign report
starts_at String ISO 8601 start timestamp
ends_at String ISO 8601 end timestamp
utm_source String utm_source every link in the campaign inherits
utm_medium String utm_medium every link in the campaign inherits
utm_campaign String Overrides the slug as the inherited utm_campaign
utm_term String utm_term every link in the campaign inherits
utm_content String utm_content every link in the campaign inherits
custom_utms Hash Arbitrary UTM defaults keyed by bare suffix
archived boolean false restores an archived campaign; true archives it
200 Updated
401 Missing or invalid token
404 Unknown campaign, or it belongs to another organisation
422 Invalid params

Archive a campaign

DELETE /v1/campaigns/:id

Archiving, not deletion: the campaign's links stay attached and its click history stays readable, so the report a finished campaign produced doesn't evaporate. An archived campaign stops counting against your plan's active-campaign cap, and drops out of GET /v1/campaigns unless you pass ?archived=true. Restore it with PATCH /v1/campaigns/:id and archived: false. Returns the archived campaign, and is idempotent.

200 Archived
401 Missing or invalid token
404 Unknown campaign, or it belongs to another organisation

Sample event payloads

GET /v1/events/samples

Returns up to 3 recent events from your account for ?event= (link.clicked, link.created or link.expired), shaped exactly like the corresponding webhook deliveries — canned sample data when your account has no matching activity yet. Built for integrations that need example data before any real event has fired (Zapier's sample step reads this endpoint).

200 Success
401 Missing or invalid token
422 Missing or unknown event type

List your webhooks

GET /v1/webhooks

Returns the authenticated organisation's webhooks, newest first. The signing secret is never included here — it is shown only once, in the response to the create call.

200 Success
401 Missing or invalid token

Create a webhook

POST /v1/webhooks

Registers an endpoint that receives a signed POST on each matching event. link.clicked fires on every recorded visit with the click's geo / device / referer / UTM payload; link.created fires when a link is created, though not for links created by a CSV import — a bulk import would fan out one event per row and overrun the per-webhook delivery rate limit, so imports are skipped entirely rather than delivered in part; link.expired fires once when a link passes its expires_at or max_clicks threshold. The response includes the signing secret — store it now, it is never returned again. Verify deliveries with the X-Slsh-Signature header (HMAC-SHA256 of the raw body, keyed on the secret).

NameTypeRequiredDescription
url String required HTTPS endpoint that will receive the POST
event_types Array Events to subscribe to. Defaults to ["link.clicked"]. Supported: link.clicked, link.created, link.expired.
201 Created
401 Missing or invalid token
422 Invalid params (non-https url, unknown event type, or per-org cap reached)

Fetch a webhook

GET /v1/webhooks/:id

Returns a single webhook by id. The signing secret is never included.

200 Success
401 Missing or invalid token
404 Unknown webhook, or it belongs to another organisation

Delete a webhook

DELETE /v1/webhooks/:id

Permanently removes the webhook. No further deliveries are attempted.

204 Deleted
401 Missing or invalid token
404 Unknown webhook, or it belongs to another organisation

List your short links

GET /v1/links

Returns the authenticated user's default-domain links, newest first. Paginated with ?page=N (1-indexed, 100/page). The response carries X-Total-Count and a standard Link header (RFC 5988) with next, prev, first, last URLs. Links on your custom domains aren't returned here — those are managed via the web app and live in a separate per-domain namespace. Pass ?campaign_id=<uuid> to return only the links filed under one campaign.

200 Success
401 Missing or invalid token
422 Unknown campaign_id

Create a short link

POST /v1/links

Creates a short link from a long URL. Slug is auto-generated if omitted.

NameTypeRequiredDescription
url String required The destination URL (http or https)
slug String Custom slug; must be globally unique on slsh.me. (Links on your custom domains have their own per-domain namespace, but those are managed via the web app — this endpoint always creates a default-domain link.)
password String Password-protect the redirect
expires_at String ISO 8601 expiry timestamp
max_clicks Integer Auto-expire after N clicks
og_title String Open Graph title
og_description String Open Graph description
campaign_id String Id of a campaign of yours to file this link under. The campaign's UTM preset is composed into the destination URL, with any utm_* you pass here winning per key. Archived campaigns can't take new links.
mask boolean Serve the destination inside a masked iframe (the short URL stays in the address bar). Pro plans only.
notify_on_mask_break boolean Email the author if a masked destination later starts blocking framing.
201 Created
401 Missing or invalid token
422 Invalid params, unknown campaign_id, or masking requested without a Pro plan

Fetch a link by slug

GET /v1/links/:slug

200 Success
401 Missing or invalid token
403 Slug belongs to another user
404 Unknown slug

Update a link

PATCH /v1/links/:slug

Updates the destination URL or metadata. The slug is immutable; any slug in the body is ignored.

NameTypeRequiredDescription
url String Replace the destination URL
password String Set or change the password
expires_at String ISO 8601 expiry timestamp
max_clicks Integer Auto-expire after N clicks
og_title String Open Graph title
og_description String Open Graph description
campaign_id String Move the link into one of your campaigns; `null` detaches it. Omit the key to leave it alone. Archived campaigns can't take new links.
mask boolean Serve the destination inside a masked iframe (the short URL stays in the address bar). Pro plans only.
notify_on_mask_break boolean Email the author if a masked destination later starts blocking framing.
200 Updated
401 Missing or invalid token
403 Slug belongs to another user
404 Unknown slug
422 Invalid params, unknown campaign_id, or masking requested without a Pro plan

Delete a link

DELETE /v1/links/:slug

Permanently deletes the link and cascades to its click history.

204 Deleted
401 Missing or invalid token
403 Slug belongs to another user
404 Unknown slug

Who am I

GET /v1/me

Returns the user and organisation this token is scoped to. The cheapest way to verify a token — integrations (Zapier among them) test credentials against this endpoint.

200 Success
401 Missing or invalid token