@recursiv/sdk 0.7.49 → 0.7.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -10
- package/dist/index.d.ts +12 -6
- package/dist/index.js +4 -0
- package/dist/realtime.d.ts +79 -9
- package/dist/realtime.js +339 -76
- package/dist/resources/agents.d.ts +28 -2
- package/dist/resources/agents.js +38 -23
- package/dist/resources/app-subscriptions.d.ts +54 -2
- package/dist/resources/app-subscriptions.js +25 -1
- package/dist/resources/auth.d.ts +198 -2
- package/dist/resources/auth.js +397 -3
- package/dist/resources/boosts.d.ts +148 -0
- package/dist/resources/boosts.js +70 -0
- package/dist/resources/chat.d.ts +33 -3
- package/dist/resources/chat.js +38 -2
- package/dist/resources/communities.d.ts +22 -10
- package/dist/resources/communities.js +19 -5
- package/dist/resources/curator.d.ts +42 -0
- package/dist/resources/deployments.d.ts +8 -3
- package/dist/resources/notifications.d.ts +74 -5
- package/dist/resources/notifications.js +38 -8
- package/dist/resources/posts.d.ts +4 -2
- package/dist/resources/posts.js +5 -1
- package/dist/resources/profiles.d.ts +45 -9
- package/dist/resources/profiles.js +54 -3
- package/dist/resources/protocols.d.ts +197 -10
- package/dist/resources/protocols.js +76 -8
- package/dist/resources/settings.d.ts +25 -3
- package/dist/resources/settings.js +28 -2
- package/dist/resources/video.d.ts +19 -0
- package/dist/resources/video.js +8 -0
- package/dist/resources/wallet.d.ts +41 -1
- package/dist/resources/wallet.js +8 -0
- package/dist/types.d.ts +201 -3
- package/llm.md +60 -16
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
|
-
import type { SingleResponse } from '../types.js';
|
|
2
|
+
import type { PaginatedResponse, SingleResponse } from '../types.js';
|
|
3
3
|
interface WalletInfo {
|
|
4
4
|
configured: boolean;
|
|
5
5
|
address: string | null;
|
|
@@ -37,6 +37,39 @@ export interface PayoutState {
|
|
|
37
37
|
payouts_enabled: boolean;
|
|
38
38
|
country: string | null;
|
|
39
39
|
}
|
|
40
|
+
/** A wallet the user linked and (optionally) verified. */
|
|
41
|
+
export interface LinkedTokenWallet {
|
|
42
|
+
chain: string;
|
|
43
|
+
address: string;
|
|
44
|
+
verified_at: string | null;
|
|
45
|
+
}
|
|
46
|
+
/** The user's MINDS token balance from the off-chain ledger. */
|
|
47
|
+
export interface TokenBalance {
|
|
48
|
+
/** Signed wei as a decimal string: it exceeds Number.MAX_SAFE_INTEGER, never parse it as a number. */
|
|
49
|
+
offchain_wei: string;
|
|
50
|
+
/** The same balance in MINDS, rounded to 6 decimal places. Display only. */
|
|
51
|
+
offchain_minds: number;
|
|
52
|
+
/** How many off-chain ledger entries make up the balance. */
|
|
53
|
+
entries: number;
|
|
54
|
+
linked_wallets: LinkedTokenWallet[];
|
|
55
|
+
base_wallet_address: string | null;
|
|
56
|
+
}
|
|
57
|
+
/** One token ledger movement. */
|
|
58
|
+
export interface TokenLedgerEntry {
|
|
59
|
+
id: string;
|
|
60
|
+
category: string | null;
|
|
61
|
+
/** Signed wei as a decimal string. */
|
|
62
|
+
amount_wei: string;
|
|
63
|
+
amount_minds: number;
|
|
64
|
+
asset: string;
|
|
65
|
+
settlement: string;
|
|
66
|
+
tx_hash: string | null;
|
|
67
|
+
counterparty_user_id: string | null;
|
|
68
|
+
subject_type: string | null;
|
|
69
|
+
subject_id: string | null;
|
|
70
|
+
status: string;
|
|
71
|
+
occurred_at: string;
|
|
72
|
+
}
|
|
40
73
|
export declare class WalletResource {
|
|
41
74
|
private client;
|
|
42
75
|
constructor(client: HttpClient);
|
|
@@ -44,6 +77,13 @@ export declare class WalletResource {
|
|
|
44
77
|
getMyWallet(): Promise<SingleResponse<WalletInfo>>;
|
|
45
78
|
/** Get wallet balance for the current user */
|
|
46
79
|
getBalance(): Promise<SingleResponse<WalletBalance>>;
|
|
80
|
+
/** The current user's MINDS token balance (off-chain ledger) and linked wallets. */
|
|
81
|
+
getTokenBalance(): Promise<SingleResponse<TokenBalance>>;
|
|
82
|
+
/** The current user's token ledger, newest first (limit up to 200, default 50). */
|
|
83
|
+
getTokenLedger(params?: {
|
|
84
|
+
limit?: number;
|
|
85
|
+
offset?: number;
|
|
86
|
+
}): Promise<PaginatedResponse<TokenLedgerEntry>>;
|
|
47
87
|
/** USD payout eligibility for the current app user (Stripe Connect). */
|
|
48
88
|
getPayouts(): Promise<SingleResponse<PayoutState>>;
|
|
49
89
|
/** Start or resume payout onboarding. Open the returned URL in a browser. */
|
package/dist/resources/wallet.js
CHANGED
|
@@ -11,6 +11,14 @@ export class WalletResource {
|
|
|
11
11
|
getBalance() {
|
|
12
12
|
return this.client.get('/wallet/balance');
|
|
13
13
|
}
|
|
14
|
+
/** The current user's MINDS token balance (off-chain ledger) and linked wallets. */
|
|
15
|
+
getTokenBalance() {
|
|
16
|
+
return this.client.get('/wallet/token-balance');
|
|
17
|
+
}
|
|
18
|
+
/** The current user's token ledger, newest first (limit up to 200, default 50). */
|
|
19
|
+
getTokenLedger(params) {
|
|
20
|
+
return this.client.get('/wallet/token-ledger', params);
|
|
21
|
+
}
|
|
14
22
|
/** USD payout eligibility for the current app user (Stripe Connect). */
|
|
15
23
|
getPayouts() {
|
|
16
24
|
return this.client.get('/wallet/payouts');
|
package/dist/types.d.ts
CHANGED
|
@@ -92,7 +92,14 @@ export interface User extends UserSummary {
|
|
|
92
92
|
posts_count: number;
|
|
93
93
|
created_at: string;
|
|
94
94
|
}
|
|
95
|
-
|
|
95
|
+
/** The caller's follow relationship with a listed account (both directions). */
|
|
96
|
+
export interface ViewerRelationship {
|
|
97
|
+
/** The caller follows this account. */
|
|
98
|
+
is_following: boolean;
|
|
99
|
+
/** This account follows the caller. */
|
|
100
|
+
follows_you: boolean;
|
|
101
|
+
}
|
|
102
|
+
export interface FollowUser extends ViewerRelationship {
|
|
96
103
|
id: string;
|
|
97
104
|
name: string;
|
|
98
105
|
username: string;
|
|
@@ -155,7 +162,10 @@ export interface RepostedPost {
|
|
|
155
162
|
created_at: string;
|
|
156
163
|
}
|
|
157
164
|
export interface PostDetail extends Post {
|
|
165
|
+
/** The newest replies (at most 50). */
|
|
158
166
|
replies: PostReply[];
|
|
167
|
+
/** Present when the thread has more replies; pass it to `posts.replies(id, { cursor })` for the next, older page. */
|
|
168
|
+
replies_next_cursor?: string | null;
|
|
159
169
|
}
|
|
160
170
|
export interface PostReply {
|
|
161
171
|
id: string;
|
|
@@ -164,8 +174,27 @@ export interface PostReply {
|
|
|
164
174
|
author: UserSummary;
|
|
165
175
|
tags: Tag[];
|
|
166
176
|
reactions_count: number;
|
|
177
|
+
score?: number;
|
|
178
|
+
user_reaction?: string | null;
|
|
179
|
+
/** This reply's own reply count, so its sub-thread can be opened. */
|
|
180
|
+
reply_count?: number;
|
|
167
181
|
created_at: string;
|
|
168
182
|
}
|
|
183
|
+
export interface ListPostRepliesParams {
|
|
184
|
+
/** 1-100, default 50. */
|
|
185
|
+
limit?: number;
|
|
186
|
+
/** `replies_next_cursor` from `posts.get`, or `meta.next_cursor` from the previous page. */
|
|
187
|
+
cursor?: string;
|
|
188
|
+
}
|
|
189
|
+
/** One page of a post's replies, newest first. */
|
|
190
|
+
export interface PostRepliesPage {
|
|
191
|
+
data: PostReply[];
|
|
192
|
+
meta: {
|
|
193
|
+
limit: number;
|
|
194
|
+
has_more: boolean;
|
|
195
|
+
next_cursor: string | null;
|
|
196
|
+
};
|
|
197
|
+
}
|
|
169
198
|
export interface CreatePostInput {
|
|
170
199
|
content: string;
|
|
171
200
|
content_format?: 'plain' | 'markdown';
|
|
@@ -214,6 +243,10 @@ export interface ListPostsParams extends PaginationParams {
|
|
|
214
243
|
export interface SearchPostsParams extends PaginationParams {
|
|
215
244
|
q: string;
|
|
216
245
|
tag_ids?: string;
|
|
246
|
+
/** `relevance` (default) or `new` (newest first). */
|
|
247
|
+
sort?: 'relevance' | 'new';
|
|
248
|
+
since?: string;
|
|
249
|
+
until?: string;
|
|
217
250
|
}
|
|
218
251
|
export type QueryParams = Record<string, unknown>;
|
|
219
252
|
export interface CommunityCreator {
|
|
@@ -232,6 +265,10 @@ export interface Community {
|
|
|
232
265
|
privacy: 'public' | 'private' | 'hidden';
|
|
233
266
|
created_by: CommunityCreator;
|
|
234
267
|
member_count: number;
|
|
268
|
+
/** Denormalized post count. */
|
|
269
|
+
post_count?: number;
|
|
270
|
+
/** Whether the caller is an accepted member. Returned by `list` and `search`. */
|
|
271
|
+
is_member?: boolean;
|
|
235
272
|
created_at: string;
|
|
236
273
|
}
|
|
237
274
|
/** Filters accepted by the communities directory. */
|
|
@@ -240,11 +277,71 @@ export interface ListCommunitiesParams extends PaginationParams {
|
|
|
240
277
|
member?: boolean;
|
|
241
278
|
/** Restrict personal keys to one organization. Project-bound keys are already scoped. */
|
|
242
279
|
organization_id?: string;
|
|
243
|
-
/** Server-side directory ordering. */
|
|
280
|
+
/** Server-side directory ordering. With `q`, name matches come first and this breaks ties. */
|
|
244
281
|
sort?: 'members' | 'active' | 'newest';
|
|
282
|
+
/**
|
|
283
|
+
* Case-insensitive text search over name and description, up to 200
|
|
284
|
+
* characters. With `member: true` it searches only the caller's own groups.
|
|
285
|
+
*/
|
|
286
|
+
q?: string;
|
|
245
287
|
}
|
|
288
|
+
/** The caller's own membership row in a group, if any. */
|
|
289
|
+
export type CommunityMembershipStatus = 'accepted' | 'pending' | 'invited' | 'banned';
|
|
246
290
|
export interface CommunityDetail extends Community {
|
|
247
291
|
post_count: number;
|
|
292
|
+
/** The caller's role when they are an accepted member (the creator is always `owner`). */
|
|
293
|
+
viewer_role?: 'owner' | 'admin' | 'moderator' | 'member' | null;
|
|
294
|
+
/** The caller's own membership state, whether or not it is accepted. */
|
|
295
|
+
membership_status?: CommunityMembershipStatus | null;
|
|
296
|
+
/** People waiting to join. Present only for moderators and up. */
|
|
297
|
+
pending_request_count?: number;
|
|
298
|
+
/** Always absent or false on a full detail; see `CommunityCard`. */
|
|
299
|
+
limited?: false;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* A private group seen from outside: `communities.get` returns this to a
|
|
303
|
+
* signed-in person who is not an accepted member (including a pending
|
|
304
|
+
* requester or a banned account), so an app can offer "Request to join" or
|
|
305
|
+
* show "Requested". The description, creator, roster, post count and posts
|
|
306
|
+
* stay with members. Hidden groups still answer 404, and so does an app's
|
|
307
|
+
* private group to a caller outside that app (an app key sees its own app).
|
|
308
|
+
*/
|
|
309
|
+
export interface CommunityCard {
|
|
310
|
+
id: string;
|
|
311
|
+
name: string;
|
|
312
|
+
slug: string;
|
|
313
|
+
image: string | null;
|
|
314
|
+
banner: string | null;
|
|
315
|
+
privacy: 'private';
|
|
316
|
+
member_count: number;
|
|
317
|
+
is_member: false;
|
|
318
|
+
viewer_role: null;
|
|
319
|
+
membership_status: CommunityMembershipStatus | null;
|
|
320
|
+
limited: true;
|
|
321
|
+
description?: undefined;
|
|
322
|
+
created_by?: undefined;
|
|
323
|
+
post_count?: undefined;
|
|
324
|
+
created_at?: undefined;
|
|
325
|
+
}
|
|
326
|
+
/** A row from `communities.related`: a public group, ranked by shared members. */
|
|
327
|
+
export interface RelatedCommunity {
|
|
328
|
+
id: string;
|
|
329
|
+
name: string;
|
|
330
|
+
slug: string;
|
|
331
|
+
description: string | null;
|
|
332
|
+
image: string | null;
|
|
333
|
+
banner: string | null;
|
|
334
|
+
privacy: 'public';
|
|
335
|
+
/** Whether the caller is an accepted member of this related group. */
|
|
336
|
+
is_member: boolean;
|
|
337
|
+
member_count: number;
|
|
338
|
+
post_count: number;
|
|
339
|
+
/** How many members this group shares with the one asked about. */
|
|
340
|
+
overlap: number;
|
|
341
|
+
}
|
|
342
|
+
/** Paging plus `q`: a case-insensitive name or username search, up to 200 characters. */
|
|
343
|
+
export interface CommunityPeopleParams extends PaginationParams {
|
|
344
|
+
q?: string;
|
|
248
345
|
}
|
|
249
346
|
export interface CommunityMember {
|
|
250
347
|
id: string;
|
|
@@ -328,8 +425,25 @@ export interface Conversation {
|
|
|
328
425
|
last_message: LastMessage | null;
|
|
329
426
|
/** Omitted by API versions deployed before message requests. */
|
|
330
427
|
request_status?: ChatRequestStatus;
|
|
428
|
+
/** The member who started it; null when it cannot be told (older threads). */
|
|
429
|
+
created_by?: string | null;
|
|
430
|
+
/** Whether `chat.deleteConversation()` is allowed for the caller. */
|
|
431
|
+
can_delete?: boolean;
|
|
432
|
+
/** Whether `chat.leave()` is allowed for the caller. */
|
|
433
|
+
can_leave?: boolean;
|
|
331
434
|
created_at: string;
|
|
332
435
|
}
|
|
436
|
+
export interface ConversationRequestResult {
|
|
437
|
+
id: string;
|
|
438
|
+
request_status: ChatRequestStatus;
|
|
439
|
+
/** False when the conversation was already in that state. */
|
|
440
|
+
changed: boolean;
|
|
441
|
+
}
|
|
442
|
+
export interface LeaveConversationResult {
|
|
443
|
+
left: true;
|
|
444
|
+
/** True when the caller was the last person in it, so the room was removed. */
|
|
445
|
+
conversation_deleted: boolean;
|
|
446
|
+
}
|
|
333
447
|
export interface ConversationDetail {
|
|
334
448
|
/** Missing on older APIs: treat as unknown, never as plaintext permission. */
|
|
335
449
|
encryption?: ConversationEncryptionPolicy;
|
|
@@ -340,6 +454,12 @@ export interface ConversationDetail {
|
|
|
340
454
|
members: ConversationMemberDetail[];
|
|
341
455
|
/** Omitted by API versions deployed before message requests. */
|
|
342
456
|
request_status?: ChatRequestStatus;
|
|
457
|
+
/** The member who started it; null when it cannot be told (older threads). */
|
|
458
|
+
created_by?: string | null;
|
|
459
|
+
/** Whether `chat.deleteConversation()` is allowed for the caller. */
|
|
460
|
+
can_delete?: boolean;
|
|
461
|
+
/** Whether `chat.leave()` is allowed for the caller. */
|
|
462
|
+
can_leave?: boolean;
|
|
343
463
|
created_at: string;
|
|
344
464
|
}
|
|
345
465
|
export interface MessageReaction {
|
|
@@ -656,6 +776,12 @@ export interface AgentChatInput {
|
|
|
656
776
|
conversation_id?: string;
|
|
657
777
|
new_conversation?: boolean;
|
|
658
778
|
}
|
|
779
|
+
/** Answer an existing message of yours again, without sending it a second time. */
|
|
780
|
+
export interface AgentChatRetryInput {
|
|
781
|
+
conversation_id: string;
|
|
782
|
+
/** Your message whose reply failed (or that you want answered again). Must be the latest one. */
|
|
783
|
+
retry_message_id: string;
|
|
784
|
+
}
|
|
659
785
|
export interface AgentChatResult {
|
|
660
786
|
message_id: string;
|
|
661
787
|
conversation_id: string;
|
|
@@ -727,6 +853,24 @@ export interface Profile {
|
|
|
727
853
|
is_ai: boolean;
|
|
728
854
|
created_at: string;
|
|
729
855
|
}
|
|
856
|
+
/** A row of a followers/following list: the profile plus the caller's relationship with it. */
|
|
857
|
+
export interface FollowListProfile extends Profile, ViewerRelationship {
|
|
858
|
+
}
|
|
859
|
+
/** An account the caller blocked. */
|
|
860
|
+
export interface BlockedProfile extends Profile {
|
|
861
|
+
blocked_at: string;
|
|
862
|
+
}
|
|
863
|
+
/** An account the caller muted. */
|
|
864
|
+
export interface MutedProfile extends Profile {
|
|
865
|
+
muted_at: string;
|
|
866
|
+
}
|
|
867
|
+
/** Paging and name/username filter for the caller's blocked and muted lists. */
|
|
868
|
+
export type ModerationListParams = {
|
|
869
|
+
limit?: number;
|
|
870
|
+
offset?: number;
|
|
871
|
+
/** Case-insensitive match on name or username. */
|
|
872
|
+
search?: string;
|
|
873
|
+
};
|
|
730
874
|
export interface ProfileDetail extends Profile {
|
|
731
875
|
followers_count: number;
|
|
732
876
|
following_count: number;
|
|
@@ -736,6 +880,12 @@ export interface ProfileMe extends Profile {
|
|
|
736
880
|
email_verified: boolean;
|
|
737
881
|
role: string;
|
|
738
882
|
two_factor_enabled: boolean | null;
|
|
883
|
+
/**
|
|
884
|
+
* Whether the account can confirm changes with a password. False for
|
|
885
|
+
* OTP-only accounts: they confirm email and two-factor changes with a code
|
|
886
|
+
* from `settings.requestConfirmationCode`. Absent on servers older than this field.
|
|
887
|
+
*/
|
|
888
|
+
has_password?: boolean;
|
|
739
889
|
followers_count: number;
|
|
740
890
|
following_count: number;
|
|
741
891
|
updated_at: string;
|
|
@@ -749,6 +899,20 @@ export interface UpdateProfileInput {
|
|
|
749
899
|
export interface SearchProfilesParams extends PaginationParams {
|
|
750
900
|
q: string;
|
|
751
901
|
}
|
|
902
|
+
export interface SearchMentionsParams {
|
|
903
|
+
/** What the member typed after `@` (a leading `@` is ignored). */
|
|
904
|
+
q: string;
|
|
905
|
+
/** Default 8, max 25. */
|
|
906
|
+
limit?: number;
|
|
907
|
+
[key: string]: unknown;
|
|
908
|
+
}
|
|
909
|
+
/** An account a composer's @-picker may offer: a person or an agent. */
|
|
910
|
+
export interface MentionCandidate extends Profile {
|
|
911
|
+
/** True for an agent (a platform or app agent, not a person's own AI). */
|
|
912
|
+
is_agent: boolean;
|
|
913
|
+
/** True when the caller follows this account; followed accounts rank first. */
|
|
914
|
+
is_following: boolean;
|
|
915
|
+
}
|
|
752
916
|
export interface LeaderboardEntry {
|
|
753
917
|
id: string;
|
|
754
918
|
name: string;
|
|
@@ -759,6 +923,8 @@ export interface LeaderboardEntry {
|
|
|
759
923
|
post_count: number;
|
|
760
924
|
reaction_count: number;
|
|
761
925
|
engagement: number;
|
|
926
|
+
/** Whether the caller follows this account. */
|
|
927
|
+
is_following?: boolean;
|
|
762
928
|
created_at: string;
|
|
763
929
|
}
|
|
764
930
|
export interface Organization {
|
|
@@ -868,7 +1034,39 @@ export interface ChangePasswordInput {
|
|
|
868
1034
|
}
|
|
869
1035
|
export interface RequestEmailChangeInput {
|
|
870
1036
|
new_email: string;
|
|
871
|
-
password
|
|
1037
|
+
/** Required for an account with a password. */
|
|
1038
|
+
password?: string;
|
|
1039
|
+
/**
|
|
1040
|
+
* Required for an account with no password (`has_password: false`): the
|
|
1041
|
+
* code from `settings.requestConfirmationCode('email_change')`, which is
|
|
1042
|
+
* sent to the CURRENT address.
|
|
1043
|
+
*/
|
|
1044
|
+
email_code?: string;
|
|
1045
|
+
}
|
|
1046
|
+
/** What a confirmation code is for. A code for one purpose never confirms another. */
|
|
1047
|
+
export type ConfirmationCodePurpose = 'email_change' | 'two_factor';
|
|
1048
|
+
export interface ConfirmationCodeSent {
|
|
1049
|
+
success: true;
|
|
1050
|
+
purpose: ConfirmationCodePurpose;
|
|
1051
|
+
/** ISO timestamp. Codes live ten minutes. */
|
|
1052
|
+
expires_at: string;
|
|
1053
|
+
}
|
|
1054
|
+
export interface RevokeOtherSessionsInput {
|
|
1055
|
+
/**
|
|
1056
|
+
* The Better Auth session token this device holds, if any. Proving it keeps
|
|
1057
|
+
* that session alive; without it every session is revoked. The calling API
|
|
1058
|
+
* key is always kept.
|
|
1059
|
+
*/
|
|
1060
|
+
keep_session_token?: string;
|
|
1061
|
+
}
|
|
1062
|
+
export interface RevokeOtherSessionsResult {
|
|
1063
|
+
success: true;
|
|
1064
|
+
/** Better Auth sessions revoked. */
|
|
1065
|
+
revoked: number;
|
|
1066
|
+
/** Other API keys revoked (within this key's app when the key is app-bound). */
|
|
1067
|
+
api_keys_revoked: number;
|
|
1068
|
+
/** Whether keep_session_token named a session of this account, which was kept. */
|
|
1069
|
+
kept_session: boolean;
|
|
872
1070
|
}
|
|
873
1071
|
export interface DeletionStatus {
|
|
874
1072
|
requested: boolean;
|
package/llm.md
CHANGED
|
@@ -162,7 +162,7 @@ await r.agents.grantProjectAccess(agent.id, {
|
|
|
162
162
|
})
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
Methods: `list`, `get`, `create`, `update`, `delete`, `chat`, `chatStream`, `chatStreamText`, `conversations`, `listDiscoverable`, `leaderboard`, `grantProjectAccess`, `revokeProjectAccess`, `resetRequestCount`, `getByClaimToken`, `claim`, `orchestratorStatus`
|
|
165
|
+
Methods: `list`, `get`, `create`, `update`, `delete`, `chat`, `chatStream`, `chatStreamText`, `retryChatStream`, `retryChatStreamText` ("Try again": `{ conversation_id, retry_message_id }` re-answers your latest message without sending it twice), `conversations`, `listDiscoverable`, `leaderboard`, `grantProjectAccess`, `revokeProjectAccess`, `resetRequestCount`, `getByClaimToken`, `claim`, `orchestratorStatus`
|
|
166
166
|
|
|
167
167
|
### r.brain — AI assistant messaging
|
|
168
168
|
|
|
@@ -371,6 +371,24 @@ Bearer auth, with a Cookie fallback for native/server runtimes.
|
|
|
371
371
|
|
|
372
372
|
Methods: `signUp`, `signIn`, `getSession`, `signOut`, `createApiKey`, `signInAndCreateKey`, `signUpAndCreateKey`, `verifyOtpAndCreateKey`
|
|
373
373
|
|
|
374
|
+
Also: `signInWithUsername` / `signInWithUsernameAndCreateKey` (handle + password, resolved on the app's network; pass `{ projectId }` as app context), `forgetPassword(input, appContext?)`, `resetPassword({ newPassword, token }, appContext?)`. Password sign-in on a two-factor account throws `AuthenticationError` with code `two_factor_required`; its `details` is `{ twoFactorToken, methods }`. Finish with `completeTwoFactorSignIn({ twoFactorToken, code, method? })` (`method: 'backup_code'` for a backup code), or `completeTwoFactorSignInAndCreateKey(input, keyInput)` after a `*AndCreateKey` sign-in. Ten wrong codes lock the account's two-factor sign-in for fifteen minutes (`ACCOUNT_TEMPORARILY_LOCKED`, `RateLimitError` with `retryAfter`).
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
// Sign-in on a two-factor account: catch two_factor_required, then send the code with the challenge.
|
|
378
|
+
try { await r.auth.signInWithUsername({ username, password }, { projectId }) } catch (e) {
|
|
379
|
+
if (e.code === 'two_factor_required') await r.auth.completeTwoFactorSignIn({ twoFactorToken: e.details.twoFactorToken, code })
|
|
380
|
+
}
|
|
381
|
+
// Two-factor: session-token calls. verify/disable rotate the session; store the returned sessionToken.
|
|
382
|
+
const { totpURI, backupCodes, sessionToken } = await r.auth.enableTwoFactor({ password }, session.token)
|
|
383
|
+
const { sessionToken: live } = await r.auth.verifyTwoFactor({ code: '123456' }, sessionToken) // turns 2FA on
|
|
384
|
+
// No password (profiles.me().has_password === false)? Confirm with an emailed code instead.
|
|
385
|
+
// Enrolling such an account answers PASSWORD_REQUIRED_FOR_TWO_FACTOR unless the server enables it.
|
|
386
|
+
await r.auth.requestTwoFactorEmailCode(live, { projectId }) // app context brands the email; then pass { emailCode } where you would pass { password }
|
|
387
|
+
await r.auth.disableTwoFactor({ emailCode: '482913' }, live)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Two-factor methods: `enableTwoFactor`, `verifyTwoFactor`, `disableTwoFactor`, `regenerateTwoFactorBackupCodes`, `requestTwoFactorEmailCode`, `completeTwoFactorSignIn`, `completeTwoFactorSignInAndCreateKey`. OAuth consent screens: `exchangeSessionForCookie(token)` (browser, sets the API-origin cookie so `/api/auth/mcp/authorize` continues without a second sign-in), `getOAuthClientInfo({ clientId, redirectUri?, scope? })` (public client name, redirect hosts, `redirect_uri_registered`, granted scopes).
|
|
391
|
+
|
|
374
392
|
**Mobile/React Native auth** (recommended — one call instead of three):
|
|
375
393
|
```typescript
|
|
376
394
|
const anonSdk = new Recursiv({ anonymous: true, baseUrl });
|
|
@@ -457,9 +475,11 @@ Methods: `list`, `get`, `getBySlug`, `create`, `update`, `delete`, `members`, `a
|
|
|
457
475
|
const { data: profile } = await r.profiles.me()
|
|
458
476
|
await r.profiles.follow('user_xxx')
|
|
459
477
|
const { data: results } = await r.profiles.search({ query: 'jane' })
|
|
478
|
+
const { data: rows } = await r.profiles.followers('user_xxx') // each row: is_following, follows_you
|
|
479
|
+
await r.profiles.mute('user_xxx') // server hides their posts and reposts of them from your feeds
|
|
460
480
|
```
|
|
461
481
|
|
|
462
|
-
Methods: `list`, `me`, `get`, `getByUsername`, `search`, `update`, `follow`, `unfollow`, `followers`, `following`, `isFollowing`, `leaderboard`
|
|
482
|
+
Methods: `list`, `me`, `get`, `getByUsername`, `search`, `mentions` (people + agents for an @-picker, follows first), `update`, `follow`, `unfollow`, `followers`, `following`, `followingIds` (your follow set as ids, cursor-paged), `isFollowing`, `related`, `leaderboard`, `block`, `unblock`, `blocked`, `mute`, `unmute`, `muted`
|
|
463
483
|
|
|
464
484
|
---
|
|
465
485
|
|
|
@@ -474,7 +494,9 @@ const { data: group } = await r.chat.createGroup({ name: 'Team', member_ids: ['u
|
|
|
474
494
|
const { data: msgs } = await r.chat.messages(dm.id, { limit: 50 })
|
|
475
495
|
```
|
|
476
496
|
|
|
477
|
-
Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `deleteConversation`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`
|
|
497
|
+
Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `deleteConversation`, `leave`, `acceptRequest`, `declineRequest`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`
|
|
498
|
+
|
|
499
|
+
Scroll back with `messages(id, { cursor: page.meta.next_cursor })`, not a growing `offset`. Conversations carry `can_delete` / `can_leave`; check them before offering Delete or Leave.
|
|
478
500
|
|
|
479
501
|
### r.e2ee — End-to-end encryption transport (flag-gated)
|
|
480
502
|
|
|
@@ -518,9 +540,14 @@ const unsub = r.realtime.onMessage((msg) => {
|
|
|
518
540
|
})
|
|
519
541
|
// Cleanup listener
|
|
520
542
|
unsub()
|
|
543
|
+
|
|
544
|
+
// Any server event, plus `connect` (fires on every reconnect: refetch what you missed) and `disconnect`
|
|
545
|
+
const off = r.realtime.on('notification', (n) => refreshBadge(n))
|
|
521
546
|
```
|
|
522
547
|
|
|
523
|
-
Events: `onMessage`, `onMessageEdit`, `onMessageDelete`, `onTyping`, `onAgentThinking`
|
|
548
|
+
Events: `onMessage`, `onMessageEdit`, `onMessageDelete`, `onTyping`, `onAgentThinking`, `on(event, fn)`
|
|
549
|
+
|
|
550
|
+
Reconnects on its own (transport drops, token expiry, refused handshakes) with jittered backoff and a fresh single-use token per attempt; listeners and joined rooms carry over, so no app-side reconnect shim is needed. It stops only on `disconnect()` or a 401/403 from the token source. `connect()` is safe to call repeatedly.
|
|
524
551
|
|
|
525
552
|
#### React / React Native specific hook
|
|
526
553
|
|
|
@@ -553,16 +580,19 @@ await r.posts.react(post.id, { type: 'fire' })
|
|
|
553
580
|
const { data: results } = await r.posts.search({ query: 'shipped', community_id: 'comm_xxx' })
|
|
554
581
|
```
|
|
555
582
|
|
|
556
|
-
Methods: `list`, `get`, `create`, `update`, `delete`, `search
|
|
583
|
+
Methods: `list`, `get`, `create`, `update`, `delete`, `search` (`sort: 'new'` for newest first), `replies(id, { limit, cursor })` (after `replies_next_cursor` on `get`), `react`, `unreact`
|
|
557
584
|
|
|
558
585
|
### r.communities
|
|
559
586
|
|
|
560
587
|
```typescript
|
|
561
588
|
const { data: c } = await r.communities.create({ name: 'Denver Devs', slug: 'denver-devs', privacy: 'public' })
|
|
562
589
|
await r.communities.join(c.id)
|
|
590
|
+
const { data: found } = await r.communities.search('denver', { limit: 10 }) // name matches first
|
|
591
|
+
const { data: mine } = await r.communities.search('book', { member: true }) // only my groups
|
|
592
|
+
const { data: people } = await r.communities.members(c.id, { q: 'ada' }) // name or username
|
|
563
593
|
```
|
|
564
594
|
|
|
565
|
-
Methods: `list`, `get`, `
|
|
595
|
+
Methods: `list` (`q`, `member`, `sort`), `search`, `get`, `related`, `members` (`q`), `create`, `join`, `leave`, `requests` (`q`), moderation (`approveMember`, `banMember`, …), `createInvite`
|
|
566
596
|
|
|
567
597
|
### r.uploads — Post and chat media
|
|
568
598
|
|
|
@@ -585,6 +615,10 @@ const publicUrl = await r.uploads.uploadMedia({
|
|
|
585
615
|
failure path; catch once at the UI boundary and tell the user. Do not repeat
|
|
586
616
|
the presign/PUT sequence in application code.
|
|
587
617
|
|
|
618
|
+
Video: check `const { data } = await r.video.entitlement()` before offering the
|
|
619
|
+
picker (`data.allowed`, `data.reason` 'tier' | 'quota', `data.remainingSeconds`),
|
|
620
|
+
then `r.video.createUpload()`, which runs the same check.
|
|
621
|
+
|
|
588
622
|
### r.chat — Messaging
|
|
589
623
|
|
|
590
624
|
```typescript
|
|
@@ -594,7 +628,9 @@ const { data: group } = await r.chat.createGroup({ name: 'Team', member_ids: ['u
|
|
|
594
628
|
const { data: msgs } = await r.chat.messages(dm.id, { limit: 50 })
|
|
595
629
|
```
|
|
596
630
|
|
|
597
|
-
Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `deleteConversation`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`
|
|
631
|
+
Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `deleteConversation`, `leave`, `acceptRequest`, `declineRequest`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`
|
|
632
|
+
|
|
633
|
+
Scroll back with `messages(id, { cursor: page.meta.next_cursor })`, not a growing `offset`. Conversations carry `can_delete` / `can_leave`; check them before offering Delete or Leave.
|
|
598
634
|
|
|
599
635
|
### r.tags
|
|
600
636
|
|
|
@@ -609,10 +645,11 @@ Methods: `list`, `get`, `create`, `delete`
|
|
|
609
645
|
|
|
610
646
|
## Platform Resources
|
|
611
647
|
|
|
612
|
-
- `r.settings` — User preferences, sessions, password changes, account deletion. With a project-bound key, `getPreferences()` includes app-scoped `content`; persist changes with `updateContent({ show_nsfw?, autoplay_video?, ai_enabled?, default_feed? })`.
|
|
648
|
+
- `r.settings` — User preferences, sessions, password changes, account deletion. `revokeOtherSessions({ keep_session_token? })` signs out every other device and keeps this key (and the proven session); `revokeAllSessions()` also revokes this key. Accounts with no password confirm `requestEmailChange({ new_email, email_code })` with a code from `requestConfirmationCode('email_change')`. With a project-bound key, `getPreferences()` includes app-scoped `content`; persist changes with `updateContent({ show_nsfw?, autoplay_video?, ai_enabled?, default_feed? })`.
|
|
613
649
|
- `r.billing` — Usage tracking, checkout sessions, portal sessions, metered billing
|
|
614
|
-
- `r.appSubscriptions` — Project-scoped consumer plans: `checkout
|
|
615
|
-
- `r.
|
|
650
|
+
- `r.appSubscriptions` — Project-scoped consumer plans: `checkout` (409 `subscription_exists` when the member already pays), `status`, `createPortalSession`, `previewTierChange({ tier })`, `changeTier({ tier, proration_date? })` (in-place, prorated, never a second subscription)
|
|
651
|
+
- `r.boosts` — Promoted posts (micro-USD): `create`, `mine`, `balance`, `pause`, `resume`, `cancel`, `recordEvent`; buy credit with `topup({ amount_cents, return_url })` then `confirmTopup(sessionId)` (credits once); admin `listPendingReview`, `review`, `grant`
|
|
652
|
+
- `r.notifications` — The notification stream: `list({ category: 'social' })` (rows carry `actor`, `reaction_type` and a `post_preview` with the viewer's `user_reaction`/`reposted_by_viewer_id` and counts), `unreadCount({ category })` for badges (`const { data } = await r.notifications.unreadCount({ category: 'social' })`), `markAsRead(id)`, `archive(id)` to dismiss, `markAllAsRead(category)` or `markAllAsRead({ category, organization_id })`, plus push token `registerToken`/`unregisterToken`. A push's `data.notificationId` is the row to mark read on tap. DMs are not in the stream.
|
|
616
653
|
- `r.uploads` — Legacy media upload URLs (prefer `r.storage` for new projects)
|
|
617
654
|
- `r.github` — GitHub integration (getRepository, getTree, getFileContent, getCommits, createFromTemplate)
|
|
618
655
|
- `r.deployments` — Deployment management (prefer `r.projects.deploy()` for new projects)
|
|
@@ -620,7 +657,7 @@ Methods: `list`, `get`, `create`, `delete`
|
|
|
620
657
|
- `r.email` — Email campaigns, batch sending, and transactional email (`r.email.send({ to, subject, html })`)
|
|
621
658
|
- `r.webhooks` — Register inbound webhook endpoints: `register()`, `list()`, `delete()`. Webhook events execute handler code in project sandbox.
|
|
622
659
|
- `r.jobs` — Scheduled cron jobs: `create({ name, cron, handler_code })`, `list()`, `update()`, `delete()`. Handlers execute in project sandbox on schedule.
|
|
623
|
-
- `r.wallet` —
|
|
660
|
+
- `r.wallet` — Wallet, MINDS tokens (`getTokenBalance`, `getTokenLedger`; wei stays a string), USD payouts (`getPayouts`, `startPayoutOnboarding`, `payoutLoginLink`)
|
|
624
661
|
- `r.freeTier` — Free tier limits and status
|
|
625
662
|
|
|
626
663
|
```typescript
|
|
@@ -629,6 +666,9 @@ if (subscription.active) {
|
|
|
629
666
|
const { data: portal } = await r.appSubscriptions.createPortalSession({ return_url: 'https://app.example.com/billing' })
|
|
630
667
|
console.log(portal.url)
|
|
631
668
|
}
|
|
669
|
+
// Switch plan in place: preview, then change with the same proration date
|
|
670
|
+
const { data: quote } = await r.appSubscriptions.previewTierChange({ tier: 'pro' })
|
|
671
|
+
await r.appSubscriptions.changeTier({ tier: 'pro', proration_date: quote.prorationDate })
|
|
632
672
|
```
|
|
633
673
|
- `r.admin` — Admin operations (user management, stats, network settings); content reports: `listReports({ status? })`, `resolveReport(id)`, `dismissReport(id)` (project-bound keys see only their app's reports)
|
|
634
674
|
- `r.inviteCodes` / `r.inviteCodesAdmin` — Invite code management
|
|
@@ -638,6 +678,7 @@ if (subscription.active) {
|
|
|
638
678
|
- Delegated social accounts: `r.protocols.nativeSocialAccounts()`, `startNativeSocialConnection({ protocol, identifier })`, `finishNativeSocialConnection({ protocol, state, code?, iss?, error? })`, `disconnectNativeSocialAccount(id)`. Example: `const { data } = await r.protocols.startNativeSocialConnection({ protocol: "bluesky", identifier: "alice.bsky.social" })`; open `data.authorizationUrl`, then complete with the same signed-in app user. Never log callback codes, send passwords, or auto-retry OAuth callbacks. Shared replies (off until schema/API deployment and live verification): `prepareNativeReply({ clientMutationId, text, parent: { protocol, nativeId, readRef? }, accountIds, media?, linkedPostAccountIds?, recoverFrom?: { batchId, deliveryIds } })`, `submitNativeReply(id, { approve: true, deliveryIds })`, `nativeReplyStatus(id)`, `nativeReplyHistory({ limit?, cursor? })`, `prepareNativeReplyRemoval(id, { deliveryIds })`, `removeNativeReply(id, { approve: true, removals })`. Review account, destination, `kind`, source, exact `wireText` and every reviewed `media` item with its alt text; connection is not publication approval. `media` takes the same `NativeMediaInput[]` as `prepareNativePost` and needs the account's `publish_media` capability. `linkedPostAccountIds` explicitly selects different-network standalone posts with a source link, never matching threaded replies or a same-network denial fallback. Preserve mutation IDs, show per-destination results and reconcile uncertain attempts without reposting. To review selected unsent or definitively rejected deliveries again, use recoverFrom with a new mutation ID, the same source/accounts and freshly reviewed text; preserve that ID on uncertain preparation. Only saved recoverable destinations qualify, current gates still apply, and replacedBy disables old sends atomically. Never restart unknown, accepted or published deliveries. The new preview still needs explicit approval. Saved delegated drafts/results: `const { data: history } = await r.protocols.nativeReplyHistory({ limit: 20 })`; use its opaque cursor for the next page and `nativeReplyStatus(item.id)` for details. This is a private read, not a write or provider refresh, and includes Nostr results marked type=nostr. Use nativeNostrReplyResult(item.id) to check that recorded event on its relay without signing or resending. Discard page state on owner/app changes. See README for examples and the callback, revocation and removal contracts.
|
|
639
679
|
- `r.protocols` — Private app safety: `sourceControls` returns current `enabled` plus paged saved choices; `setSourceControl`, `onSourceControlChange` and `reportSource` share app scope. Example: `const choices = new Map(); const stop = r.protocols.onSourceControlChange(change => choices.set(JSON.stringify([change.source, change.control]), change.enabled));` then `await r.protocols.setSourceControl({ source: { protocol: "bluesky", kind: "actor", nativeId: "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa" }, control: "mute", enabled: true, scope: "minds" });` and call `stop(); choices.clear()` on account change/unmount. Notifications follow an exact server confirmation, never an uncertain response. These preferences and reports apply in Minds, not on the remote network. Keyless post links: `const visitor = new Recursiv({ anonymous: true }); const { data: thread } = await visitor.protocols.getSharedConversation(projectId, { protocol: 'nostr', post: publicEventId })`; `sharedNetworks(projectId)` lists public conversation capabilities. Only configured apps; app moderation and rate limits apply, no member state or write authority. Member public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. Example after selecting a returned source post: `const { data: thread } = await r.protocols.getPublicConversation({ protocol: post.protocol, post: post.nativeId, readRef: post.readRef })`. Render `focus`, oldest-first `ancestors` and direct `replies`; `public_provider_window` never promises every reply, and `limited` marks known omissions. Requires both `users:read` and `posts:read` on the current human app key. Protocol discovery/settings: `const { data: adapters } = await r.protocols.list()` returns bounded `{ id, name, icon, color, maturity, capabilities, limitations }` metadata; `supported` means implemented, not configured, healthy, authorized, or enabled. Methods: `list`, `status`, `listCandidates`, `getCandidate`, `search`, `getSettings`, `refresh`, `updateSettings`.
|
|
640
680
|
- Native Nostr: `const { data: preview } = await r.protocols.prepareNativeNostr(identityId, { action: "publish", content: draft })`; review and sign the exact public event, then `submitNativeNostr(identityId, { intent: preview.intent, event, approve: true })`. Preparation/submission never auto-retry. After uncertainty, use `getNativeNostrPost(identityId, preview.postId ?? preview.eventId)` before a user-selected retry of the identical intent/event: deletion checks the original note's `postId`, publication checks `eventId`. Check `nativeNostrAccount()` enablement first; keys stay with the signer. For replies, after `nativeNostrAccount()` advertises `reply_text_note`, use `prepareNativeNostr(identityId, { action: "reply", content: draft, parentId: post.nativeId })`. Review returned parent/root/participants/relay and exact event, sign with the connected client signer, then `submitNativeNostr(identityId, { intent, event, approve: true })`. Requires users:read, posts:read and posts:write. Kind1 public notes only; reply enablement and shared UI/live proof remain pending. Do not inherit arbitrary notification tags or relay hints. Signing mutations never auto-retry; check `getNativeNostrPost(identityId, eventId)` after uncertainty before a user-selected identical-event retry.
|
|
681
|
+
- Hosted Bluesky account on the app's own PDS: `const { data } = await r.protocols.createHostedIdentity({ protocol: 'atproto', handle: 'alice', email })`. Only the label is sent; `data.handle` carries the domain and `data.password` is returned this once, never stored. Never auto-retried. Either the account is finished or it is removed again. 409 `handle_unavailable` = choose another label; 503 `handle_dns_unavailable`/`handle_unresolvable`/`handle_unverified` = operator setup, retry later unchanged.
|
|
641
682
|
- Anonymous discovery: `const visitor = new Recursiv({ anonymous: true }); const { data } = await visitor.protocols.getSharedTrends(projectId, { protocol: 'nostr', limit: 20, discovery: true })`. Use `sharedNetworks(projectId)` capabilities; open a returned topic with `getSharedTrendFeed(projectId, { protocol: 'bluesky', topic: trend.nativeId, cursor })`. Configured apps only; no account authority or writes. App moderation and per-IP/app rate limits apply. Categories and Jev layouts reuse cached judgments without initiating paid inference; handle partial categories and finite provider windows.
|
|
642
683
|
- Public trends: require `public_trends` from `publicNetworks()`, then `getPublicTrends({ protocol, limit?, cursor? })`; render `kind: topics` as source topics and `kind: posts` as source posts. With `trend_feed`, open only a returned current topic using `getPublicTrendFeed({ protocol, topic: trend.nativeId, limit?, cursor? })`. Show provider, observedAt and provider_ranked_window coverage; null rankingWindow is unknown, not a daily/global ranking. Preserve source order, canonical IDs and readRef, and opaque cursors. Topic catalogs do not paginate; Mastodon trends stop at 200 source positions. Both methods require users:read plus posts:read on the current human app key; Nostr trends are unsupported. Propagate errors; trend_unavailable means refresh the topic list. No ingestion or native writes.
|
|
643
684
|
- `listCandidates({ protocol?, limit?, cursor? })` and `getCandidate(id)` are read-only quarantine review, not published-post search. Both require `admin` scope, a project-bound key, a live admin role, and existing project-admin access. The key supplies app/network scope; neither method accepts another project or network. They never invoke refresh, change settings, or publish.
|
|
@@ -790,12 +831,15 @@ await r.agents.create({ name: 'Bot', model: 'anthropic/claude-sonnet-4' }) // fa
|
|
|
790
831
|
await r.agents.create({ name: 'Bot', username: 'bot_1', model: 'anthropic/claude-sonnet-4',
|
|
791
832
|
system_prompt: '...', organization_id: orgId })
|
|
792
833
|
|
|
793
|
-
// ❌ WRONG:
|
|
794
|
-
await r.communities.get('comm_xxx')
|
|
834
|
+
// ❌ WRONG: assuming get() always returns the full group
|
|
835
|
+
const { data } = await r.communities.get('comm_xxx')
|
|
836
|
+
data.created_by.name // undefined on a private group you are not in
|
|
795
837
|
|
|
796
|
-
// ✅ RIGHT
|
|
797
|
-
try {
|
|
798
|
-
|
|
838
|
+
// ✅ RIGHT: a private group you're not in is a card (limited: true); hidden or missing is 404
|
|
839
|
+
try {
|
|
840
|
+
const { data } = await r.communities.get('comm_xxx')
|
|
841
|
+
if (data.limited) { /* show name + "Request to join" / membership_status 'pending' */ }
|
|
842
|
+
} catch (e) { if (e.status === 404) { /* hidden or doesn't exist */ } }
|
|
799
843
|
```
|
|
800
844
|
|
|
801
845
|
---
|