DropInServer
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new DropInServer(opts, fetchFn?): DropInServer;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
opts |
DropInServerOptions |
fetchFn? |
(input, init?) => Promise<Response> |
Returns
Section titled “Returns”DropInServer
Properties
Section titled “Properties”activities
Section titled “activities”readonly activities: object;Activity-level edits. Use this to fix ONE activity’s own body — a typo, a corrected
caption. For data shared across many activities, use objects instead: patching each
activity is N writes where an object update is one.
body.refs (optional, top-level — not a custom. path) replaces the activity’s
refs array wholesale, including refs: [] to clear it. This is the backfill path
for an activity written before objects existed: it can adopt refs after the fact
without the delete-and-repost that would otherwise burn its foreign_id and
re-fan-out to every follower.
patch: <TCustom>(activityId, body, opts) => Promise<Activity<TCustom>>;Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
activityId |
string |
body |
PatchBody |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<Activity<TCustom>>
Example
Section titled “Example”await dropin.activities.patch('a1', { refs: ['session:1234'] })readonly batch: object;Cold-start import (server-token only, ≤100 items/call, quiet — no notifications,
live pings, or webhooks fire for imported history; see the “Migrating existing
data” guide). Per-item results: partial failure is still a 200, so check each
results[i].ok. Rerunning a batch is safe when activities carry foreign_id +
time.
activities
Section titled “activities”activities: (activities, opts) => Promise<BatchResponse>;QUIET: imported activities land in feeds but fire no notifications, live pings,
or activity.added webhooks.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
activities |
object[] |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<BatchResponse>
follows
Section titled “follows”follows: (follows, opts) => Promise<BatchResponse>;QUIET: the edges are created but the followed feeds get NO notification and no
follow.added webhook. For a follow that just happened in your app, use
DropInServer.userFollow / feed().follow() instead.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
follows |
object[] |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<BatchResponse>
objects
Section titled “objects”objects: (objects, opts) => Promise<BatchResponse>;Bulk upsert objects. Idempotent — safe to re-run. Check results[i].ok.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
objects |
object[] |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<BatchResponse>
userFollows
Section titled “userFollows”userFollows: (pairs, opts) => Promise<BatchResponse>;The 95% case, without feed-ref plumbing: “alice follows bob” expands to
{ source: 'timeline:alice', target: 'user:bob' } — alice’s home feed pulls
from bob’s wall. Use follows directly for non-user feed graphs.
QUIET: bob is NOT notified that alice followed him, and no follow.added webhook
fires. That is right for backfilling an existing social graph and wrong for
mirroring a follow a user just made — use DropInServer.userFollow for that.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
pairs |
object[] |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<BatchResponse>
users: (users, opts) => Promise<BatchResponse>;QUIET: imported users are created without any side effect.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
users |
object[] |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<BatchResponse>
notifications
Section titled “notifications”readonly notifications: object;Read and mark a user’s notifications from your backend — for sending push
notifications or emails, or building an admin view. A server token has no identity,
so every call names the owner it acts for; omitting it is FORBIDDEN, never a
cross-user leak.
list: (q, opts) => Promise<NotificationPage>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ cursor?: string; limit?: number; next?: string; owner: string; } |
q.cursor? |
string |
q.limit? |
number |
q.next? |
string |
q.owner |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<NotificationPage>
markRead
Section titled “markRead”markRead: (q, opts) => Promise<void>;Mark specific notifications read, or ALL of the owner’s when ids is omitted or empty.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ ids?: string[]; owner: string; } |
q.ids? |
string[] |
q.owner |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
markSeen
Section titled “markSeen”markSeen: (q, opts) => Promise<void>;Mark specific notifications seen, or ALL of the owner’s when ids is omitted or empty.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ ids?: string[]; owner: string; } |
q.ids? |
string[] |
q.owner |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
objects
Section titled “objects”readonly objects: object;Objects: data many activities share. Update one row and every timeline carrying a ref to it is fresh on the next read — no re-fan-out, regardless of how many activities point at it. Server-token only, because one object is shared by many activities.
get: <TCustom>(type, id, opts) => Promise<DropInObject<TCustom>>;Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
type |
string |
id |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<DropInObject<TCustom>>
getMany
Section titled “getMany”getMany: <TCustom>(refs, opts) => Promise<Record<string, DropInObject<TCustom>>>;Read up to 100 objects in ONE request, keyed type:id. Mirrors
@dropinnodex/client’s objects.getMany — the same route, so a server-side
revalidation (a cache warm, a webhook handler checking what moved) costs one
request rather than one per object.
Refs with no stored object are absent from the map rather than an error, matching
the feed-read objects sidecar. An empty list costs zero requests.
Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
refs |
string[] |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<Record<string, DropInObject<TCustom>>>
Example
Section titled “Example”const fresh = await dropin.objects.getMany(['session:1234', 'session:5678'])fresh['session:1234']?.custom.spots_leftpatch: <TCustom>(type, id, body, opts) => Promise<DropInObject<TCustom>>;Merges into custom. The object must exist — use upsert to create.
Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
type |
string |
id |
string |
body |
PatchBody |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<DropInObject<TCustom>>
remove
Section titled “remove”remove: (type, id, opts) => Promise<void>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
type |
string |
id |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
upsert
Section titled “upsert”upsert: <TCustom>(type, id, custom, opts) => Promise<DropInObject<TCustom>>;Replaces custom wholesale, creating the object if absent.
Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
type |
string |
id |
string |
custom |
TCustom |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<DropInObject<TCustom>>
Example
Section titled “Example”await dropin.objects.upsert('session', '1234', { spots_left: 2 })// every activity with refs: ['session:1234'] now renders 2promoted
Section titled “promoted”readonly promoted: object;Promoted activities: content that stays visible regardless of the follow graph and recency — an under-filled event, an announcement, a sponsor post, a member spotlight. Server-token only; creating one is a backend operation, usually automated.
Nothing is fanned out. One row serves every eligible reader, which is why retracting or expiring is instant and costs nothing.
Targeting is the follow graph. We store no user attributes, so an audience is
expressed as feeds. Create a feed like city:belgrade, follow your users into it
from your backend, then target it:
await dropin.batch.follows([{ source: 'timeline:alice', target: 'city:belgrade' }])await dropin.promoted.create({ actor: 'system:fcurban', verb: 'promote', object: 'game:8842', custom: { text: 'Thursday 20:00 — 6 spots left' }, audience: ['city:belgrade'], expires_at: kickoffIso, // stops serving itself; no cleanup})Eligible rows arrive in the promoted array of a feed read’s FIRST page (never
inside results, never affecting the cursor). That array is the eligible SET, not
a slot assignment — the client caches it and decides placement.
This is promoted content, not an ad platform: no bidding, no demographic targeting, no viewability tracking. The label your users see (“Sponsored”, “Featured”) — and any disclosure obligation that comes with paid placement — is yours to render.
create
Section titled “create”create: <TCustom>(input, opts) => Promise<PromotedActivityRecord<TCustom>>;Promote something. Throws CONFLICT past 100 live rows (an abuse guard, not a plan limit).
Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
input |
PromotedActivityInput<TCustom> |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<PromotedActivityRecord<TCustom>>
list: <TCustom>(q, opts) => Promise<Page<PromotedActivityRecord<TCustom>>>;Your inventory, newest first — retracted rows included, each with its served_count.
Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ limit?: number; next?: string; } |
q.limit? |
number |
q.next? |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<Page<PromotedActivityRecord<TCustom>>>
remove
Section titled “remove”remove: (id, opts) => Promise<void>;Stop serving it. Takes effect on the next feed read; retracting twice is NOT_FOUND.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
id |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
reactions
Section titled “reactions”readonly reactions: object;Admin reaction ops (server token deletes any user’s reaction). Adding a reaction is deliberately absent: a reaction needs an acting user, and a server token has no identity — mint a user token for that.
delete
Section titled “delete”delete: (reactionId, opts) => Promise<void>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
reactionId |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
list: (activityId, q, opts) => Promise<Page<Reaction>>;A page of reactions on an activity, newest first. kind filters server-side.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
activityId |
string |
q |
{ cursor?: string; kind?: string; limit?: number; next?: string; } |
q.cursor? |
string |
q.kind? |
string |
q.limit? |
number |
q.next? |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”webhooks
Section titled “webhooks”readonly webhooks: object;Outbound webhooks. Server-token only — this is where you register the endpoints feed
events are delivered to. Storage, HMAC-SHA256 signing, and retries are handled for
you; create returns the signing secret to verify deliveries with.
create
Section titled “create”create: (d, opts) => Promise<WebhookDestination>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
d |
{ url: string; } |
d.url |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<WebhookDestination>
list: (opts) => Promise<WebhookDestination[]>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<WebhookDestination[]>
remove
Section titled “remove”remove: (id, opts) => Promise<void>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
id |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
Methods
Section titled “Methods”createServerToken()
Section titled “createServerToken()”createServerToken(opts?): Promise<string>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
opts |
TokenOptions |
Returns
Section titled “Returns”Promise<string>
createUserToken()
Section titled “createUserToken()”createUserToken(userId, opts?): Promise<string>;Local HMAC. Zero network calls — tokens are minted entirely on your server.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
userId |
string |
opts |
TokenOptions |
Returns
Section titled “Returns”Promise<string>
feed()
Section titled “feed()”feed(group, id): object;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
group |
string |
id |
string |
Returns
Section titled “Returns”addActivity
Section titled “addActivity”addActivity: <TCustom>(a, opts) => Promise<Activity<TCustom>>;Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
a |
{ actor?: string; custom?: TCustom; foreign_id?: string | null; object: string; refs?: string[]; target?: string | null; time?: string; verb: string; } |
- |
a.actor? |
string |
Server tokens set the actor explicitly (e.g. “user:alice”); user tokens have it overwritten by the gateway (spec §5). |
a.custom? |
TCustom |
- |
a.foreign_id? |
string | null |
- |
a.object |
string |
- |
a.refs? |
string[] |
Objects this activity points at, as type:id. Max 4. Resolved into the feed read’s objects sidecar — see dropin.objects. |
a.target? |
string | null |
- |
a.time? |
string |
- |
a.verb |
string |
- |
opts |
RequestOptions |
- |
Returns
Section titled “Returns”Promise<Activity<TCustom>>
follow
Section titled “follow”follow: (targetGroup, targetId, opts) => Promise<void>;Create a follow edge, LOUD: a user: target gets a follow notification and a
follow.added webhook fires. This is the call for a follow that just happened in
your app — batch.follows is the quiet import path and notifies nobody.
Idempotent: re-following an existing edge writes nothing and notifies nobody, so an at-least-once trigger can safely deliver twice.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
targetGroup |
string |
targetId |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
followers
Section titled “followers”followers: (q, opts) => Promise<Page<Follow>>;Who follows this feed, newest edge first.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ cursor?: string; limit?: number; next?: string; } |
q.cursor? |
string |
q.limit? |
number |
q.next? |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”following
Section titled “following”following: (q, opts) => Promise<Page<Follow>>;Who this feed follows, newest edge first.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ cursor?: string; limit?: number; next?: string; } |
q.cursor? |
string |
q.limit? |
number |
q.next? |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”followStats
Section titled “followStats”followStats: (opts) => Promise<FollowStats>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<FollowStats>
get: <TCustom>(q, opts) => Promise<FeedPage<TCustom>>;Type Parameters
Section titled “Type Parameters”| Type Parameter | Default type |
|---|---|
TCustom |
Record<string, unknown> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ cursor?: string; limit?: number; next?: string; } |
q.cursor? |
string |
q.limit? |
number |
q.next? |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<FeedPage<TCustom>>
head: (opts) => Promise<{ changed: number | null; latest: string | null;}>;Cheap change signal for a backend poller: redis-only, no tenant-DB read. The same
shape as @dropinnodex/client’s feed().head() — two independent fields:
latest— opaque token for NEW activities. Compare with the last value you acted on; null means nothing new.changed— tenant mutation counter, covering the changeslatestcannot report: an activity edited, a reaction moved, an object written. Unchanged since your last revalidation means you can skip re-reading the page and its objects entirely. 0 means nothing has ever been mutated here;nullmeans unknown (a corrupted counter) — revalidate rather than assume.
Use this from your server when a long-running watcher needs to know whether a feed moved without paying for a full page read on every poll — a bot pulling posts for a downstream system, a webhook-style revalidation sweep.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<{
changed: number | null;
latest: string | null;
}>
removeActivity
Section titled “removeActivity”removeActivity: (activityId, opts) => Promise<void>;Soft-delete an activity. A server token may remove an activity from ANY feed — the origin-feed authority check applies to user tokens only — which is what makes this usable for moderation and for cleaning up content deleted in your own app.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
activityId |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
suggestions
Section titled “suggestions”suggestions: (q, opts) => Promise<{ results: Suggestion[];}>;Who this feed should follow — friends-of-friends by mutual overlap, topped up by popularity. A capped top-N, so there is no cursor.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
q |
{ limit?: number; } |
q.limit? |
number |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<{
results: Suggestion[];
}>
unfollow
Section titled “unfollow”unfollow: (targetGroup, targetId, opts) => Promise<void>;Remove a follow edge. Same argument shape as follow.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
targetGroup |
string |
targetId |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
revokeUserTokens()
Section titled “revokeUserTokens()”revokeUserTokens(userId, opts?): Promise<void>;Invalidates all of a user’s existing tokens immediately. The revocation is recorded server-side and enforced from the next request onward, so tokens already handed to a browser stop working without waiting for them to expire.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
userId |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
upsertUser()
Section titled “upsertUser()”upsertUser(user, opts?): Promise<{ custom: Record<string, unknown>; id: string;}>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
user |
{ custom?: Record<string, unknown>; id: string; } |
user.custom? |
Record<string, unknown> |
user.id |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<{
custom: Record<string, unknown>;
id: string;
}>
userFollow()
Section titled “userFollow()”userFollow(pair, opts?): Promise<void>;“alice follows bob”, LOUD — the notifying counterpart of batch.userFollows, for a
follow that just happened rather than one being imported. Expands to
timeline:alice → user:bob; bob is notified and follow.added fires.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
pair |
{ follower: string; following: string; } |
pair.follower |
string |
pair.following |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>
userUnfollow()
Section titled “userUnfollow()”userUnfollow(pair, opts?): Promise<void>;The inverse of DropInServer.userFollow.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
pair |
{ follower: string; following: string; } |
pair.follower |
string |
pair.following |
string |
opts |
RequestOptions |
Returns
Section titled “Returns”Promise<void>