Reference
SDK reference
Every method of the @slingdrop/sdk TypeScript SDK.
Install#
npm i @slingdrop/sdkESM only, with TypeScript types. Runs in Node 20 or later and modern browsers (it uses fetch, Blob and Web Crypto). Keep API keys on the server; in a browser, use it without a key or with signed upload URLs.
The client#
import { Slingdrop } from "@slingdrop/sdk";
const sd = new Slingdrop({
apiKey: process.env.SLINGDROP_API_KEY, // sd_…
// baseUrl: "https://slingdrop.my", // default; requests go to <baseUrl>/api/v1
// fetch: myFetch, // optional custom fetch (tests, proxies)
});Without an API key only drops.create works: it sends an anonymous drop (public or passcode, up to 24 hours), like the website does for signed-out visitors. sd.anonymous tells you which mode you're in.
| Method | Calls | Returns |
|---|---|---|
me() | GET /me | { id, email, name, plan, limits } |
request<T>(path, init?) | Any /v1 path | The parsed JSON. Use it for endpoints without a helper. |
drops#
| Method | Calls | Returns |
|---|---|---|
drops.create(files, options?) | POST /drops + uploads | { drop, shareUrl, ownerKey } |
drops.addVersion(id, files, note?) | POST /drops/{id}/versions + uploads | { id, number } |
drops.list(status?) | GET /drops?status= ("active" or "all") | Drop[] (with fileCount) |
drops.get(id) | GET /drops/{id} | Drop with files and versions |
drops.update(id, patch) | PATCH /drops/{id} | The updated drop |
drops.revoke(id) | POST /drops/{id}/revoke | { ok } |
drops.delete(id, { forever? }) | DELETE /drops/{id} | { ok, trashed } |
drops.analytics(id, days?) | GET /drops/{id}/analytics (default 30 days) | The report |
drops.comments(id) | GET /drops/{id}/comments | Comments with anchors |
drops.create#
files is a list of { name, data: Blob, type? }. A / in name keeps folder structure. In Node, await fs.openAsBlob(path) reads big files lazily. Uploads use multipart with 4 parts in flight for files over 10 MB, and retry with backoff.
| Option | Type | Notes |
|---|---|---|
title | string | |
purpose | "share" | "feedback" | Default "share" (the REST API's default is "feedback"). |
expiry | "1h" … "90d" | "forever" | "burn" | Default forever for share drops, 7d for feedback, 24h without a key. |
passcode | string | Makes it a passcode drop. |
recipients | string[] | Emails; makes it private (viewer role). |
vault | boolean | End-to-end encrypts in this process. The key is only in the returned shareUrl (after #k=). |
allowDownload | boolean | Default true. |
allowComment | boolean | Defaults to on for feedback drops. |
allowEdit | boolean | |
requireApproval | boolean | |
folderId | string | One of folders.list(). |
source | "api" | "cli" | "mcp" | Default api. |
organizationId | string | Send as a team. |
onProgress | (loaded, total) => void | Upload progress in bytes. |
signal | AbortSignal | Cancel the upload. |
const { shareUrl } = await sd.drops.create([{ name: "contract.pdf", data }], { vault: true, expiry: "7d" });
// shareUrl = https://slingdrop.my/d/…#k=… ← only you have this key. Slingdrop can't read the file.folders#
| Method | Calls | Returns |
|---|---|---|
folders.list() | GET /folders | Folder[]: id, name, color, sortOrder, dropCount, createdAt |
folders.create(name, color?) | POST /folders | Folder |
File a drop with drops.update(id, { folderId }) (null unfiles it). Rename or delete folders with sd.request; see Folders.
reviews#
| Method | Calls | Returns |
|---|---|---|
reviews.start(dropId, opts?) | POST /drops/{id}/review. { agentName?, question?, client?, createdVia? } | ReviewState |
reviews.feedback(dropId, { since?, wait? }) | GET /drops/{id}/feedback. wait up to 60 seconds. | ReviewState |
reviews.round(dropId, { note?, resolves? }) | POST /drops/{id}/rounds, after addVersion | ReviewState |
reviews.reply(dropId, commentId, text) | POST …/comments/{commentId}/replies | { id } |
reviews.resolve(dropId, commentId, resolved?) | POST …/comments/{commentId}/resolve | { ok } |
reviews.approvals(dropId) | GET /drops/{id}/approvals | Approvals |
The loop, the ReviewState shape and a full example are in Reviews & approvals.
hosting#
| Method | Calls | Returns |
|---|---|---|
hosting.spaces() | GET /hosting/spaces | HostingSpace[] |
hosting.createSpace(slug?) | POST /hosting/spaces | HostingSpace (your default space when no slug) |
hosting.deleteSpace(space) | DELETE /hosting/spaces/{space} | { ok } |
hosting.usage() | GET /hosting/usage | Storage, bandwidth, resizes this month |
hosting.put(space, path, data, opts?) | Upload (parts over 95 MB). { contentType?, immutable?, template?, onProgress? } | HostedObject with url |
hosting.list(space, { prefix?, after?, limit? }) | GET /hosting/{space}/objects | { objects, next } |
hosting.delete(space, path) | DELETE /hosting/{space}/objects/{path} | { ok } |
hosting.createUploadUrl(space, opts?) | POST /hosting/{space}/upload-urls. { path?, prefix?, filename?, contentType?, maxBytes?, expiresIn? } | UploadUrl |
hosting.signUrl(space, path, { expiresIn? }) | POST /hosting/{space}/sign (60 s to 7 days, default 1 hour) | { url, signed, expiresAt, template } |
hosting.access(space) | GET /hosting/{space}/access | { defaultTemplateId, rules } |
hosting.setAccess(space, access) | PUT /hosting/{space}/access | SpaceAccess |
hosting.setFileAccess(space, path, template) | PATCH /hosting/{space}/objects/{path}; null clears it | The file's effective access |
hosting.templates#
| Method | Calls | Returns |
|---|---|---|
templates.list() | GET /hosting/templates | AccessTemplate[] |
templates.create(input) | POST /hosting/templates | AccessTemplate plus secret (signed and webhook templates, shown once) |
templates.update(id, patch) | PATCH /hosting/templates/{id} | AccessTemplate |
templates.delete(id) | DELETE /hosting/templates/{id} | { ok } |
templates.test(id, { token, path, jwt? }) | POST /hosting/templates/{id}/test | Whether the CDN would allow that token for that path, and why |
templates.rotateSecret(id) | POST /hosting/templates/{id}/rotate | The new secret |
templates.setDefault(id | null) | PUT /hosting/default-template | { ok } (null = Public) |
Guides: File hosting and Access rules.
requests#
| Method | Calls | Returns |
|---|---|---|
requests.list() | GET /requests | File requests |
requests.create(input) | POST /requests. { title, instructions?, items?, deadlineAt?, visibility?, passcode?, allowedEmails? } | { id, url } |
webhooks#
| Method | Calls | Returns |
|---|---|---|
webhooks.list() | GET /webhooks | { data, events } |
webhooks.create(url, events) | POST /webhooks | { id, secret } (secret shown once) |
webhooks.delete(id) | DELETE /webhooks/{id} | { ok } |
webhooks.deliveries(id) | GET /webhooks/{id}/deliveries | Recent attempts |
Standalone helpers#
verifyWebhook(secret, signature, body, toleranceSec?)#
Checks a Slingdrop-Signature header against the raw body, in constant time. Rejects deliveries more than toleranceSec (default 300) seconds old. Returns a boolean.
import { verifyWebhook } from "@slingdrop/sdk";
const body = await req.text(); // the raw body, before JSON.parse
const ok = await verifyWebhook(process.env.SLINGDROP_WEBHOOK_SECRET!, req.headers.get("slingdrop-signature"), body);signHostedUrl(url, secret, { expiresIn? })#
Signs a hosted file URL locally with a signed-links template's secret, no API call. expiresIn is in seconds (default 3600).
import { signHostedUrl } from "@slingdrop/sdk";
const url = await signHostedUrl("https://cdn.slingdropcontent.my/acme/private/report.pdf", process.env.SLINGDROP_SIGNING_SECRET!, { expiresIn: 600 });
// → …/acme/private/report.pdf?exp=1767225600&sig=…Errors#
Failed calls throw SlingdropError, with the server's message, the HTTP status and a code such as rate_limited or plan_required.
import { SlingdropError } from "@slingdrop/sdk";
try {
await sd.drops.create(files, { expiry: "forever" });
} catch (err) {
if (err instanceof SlingdropError && err.code === "storage_full") {
// tell the user to free up space
} else throw err;
}Exported types#
SlingFile, CreateOptions, Drop, Folder, ReviewState, ReviewComment, HostedObject, HostingSpace, HostingUsage, UploadUrl, SignedUrl, SpaceAccess, AccessMode, AccessTemplate, AccessTemplateInput, JwtConfig, JwtProvider, JwtRule and TemplateTestResult.
Prefer another language? Generate a client from the OpenAPI spec, or try requests in the API reference.