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.
Response headers
| Name | Description |
|---|---|
| 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 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.
Responses
| 200 | Success |
| 401 | Missing or invalid token |
Create a short link
POST
/v1/links
Creates a short link from a long URL. Slug is auto-generated if omitted.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
| 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 | |
| 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. | |
Responses
| 201 | Created |
| 401 | Missing or invalid token |
| 422 | Invalid params, or masking requested without a Pro plan |
Fetch a link by slug
GET
/v1/links/:slug
Responses
| 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.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
| 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 | |
| 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. | |
Responses
| 200 | Updated |
| 401 | Missing or invalid token |
| 403 | Slug belongs to another user |
| 404 | Unknown slug |
| 422 | Invalid params, or masking requested without a Pro plan |
Delete a link
DELETE
/v1/links/:slug
Permanently deletes the link and cascades to its click history.
Responses
| 204 | Deleted |
| 401 | Missing or invalid token |
| 403 | Slug belongs to another user |
| 404 | Unknown slug |
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.
Responses
| 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. Today the only event is `link.clicked`, fired on every recorded visit with the click's geo / device / referer / UTM payload. 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).
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
| 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. | |
Responses
| 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.
Responses
| 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.
Responses
| 204 | Deleted |
| 401 | Missing or invalid token |
| 404 | Unknown webhook, or it belongs to another organisation |
List clicks for a link
GET
/v1/links/:link_slug/clicks
Returns the click stream (visits) for a default-domain link you own, 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.
Responses
| 200 | Success |
| 401 | Missing or invalid token |
| 403 | Link belongs to another user |
| 404 | Unknown link slug |