@squadbase/vite-server 0.1.3 → 0.1.4-dev.0

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.
@@ -78,72 +78,255 @@ function createClient(params) {
78
78
  );
79
79
  }
80
80
  }
81
+ function buildQuery(params2) {
82
+ if (!params2) return "";
83
+ const entries = Object.entries(params2).filter(
84
+ ([, v]) => v !== void 0 && v !== null
85
+ );
86
+ if (entries.length === 0) return "";
87
+ const usp = new URLSearchParams();
88
+ for (const [k, v] of entries) usp.set(k, String(v));
89
+ return `?${usp.toString()}`;
90
+ }
91
+ async function httpJson(method, path2, label, body) {
92
+ const res = await fetch(`${BASE_URL}${path2}`, {
93
+ method,
94
+ headers: authHeaders(),
95
+ body: body === void 0 ? void 0 : JSON.stringify(body)
96
+ });
97
+ await assertOk(res, label);
98
+ return await res.json();
99
+ }
100
+ async function httpVoid(method, path2, label) {
101
+ const res = await fetch(`${BASE_URL}${path2}`, {
102
+ method,
103
+ headers: authHeaders()
104
+ });
105
+ await assertOk(res, label);
106
+ }
107
+ function queryBody(options) {
108
+ const body = {};
109
+ if (options?.filter) body.filter = options.filter;
110
+ if (options?.sorts) body.sorts = options.sorts;
111
+ if (options?.limit) body.limit = options.limit;
112
+ if (options?.offset) body.offset = options.offset;
113
+ return body;
114
+ }
115
+ const enc = encodeURIComponent;
81
116
  return {
82
117
  request(path2, init) {
83
118
  const url = `${BASE_URL}${path2}`;
84
119
  const headers = new Headers(init?.headers);
85
120
  headers.set("Authorization", `Bearer ${apiKey}`);
86
- headers.set("Content-Type", "application/json");
121
+ if (!headers.has("Content-Type"))
122
+ headers.set("Content-Type", "application/json");
87
123
  return fetch(url, { ...init, headers });
88
124
  },
89
- async listObjects() {
90
- const res = await fetch(`${BASE_URL}/objects`, {
91
- method: "GET",
92
- headers: authHeaders()
93
- });
94
- await assertOk(res, "listObjects");
95
- return await res.json();
96
- },
97
- async listAttributes(object) {
98
- const res = await fetch(
99
- `${BASE_URL}/objects/${encodeURIComponent(object)}/attributes`,
100
- { method: "GET", headers: authHeaders() }
101
- );
102
- await assertOk(res, "listAttributes");
103
- return await res.json();
104
- },
105
- async queryRecords(object, options) {
106
- const body = {};
107
- if (options?.filter) body.filter = options.filter;
108
- if (options?.sorts) body.sorts = options.sorts;
109
- if (options?.limit) body.limit = options.limit;
110
- if (options?.offset) body.offset = options.offset;
111
- const res = await fetch(
112
- `${BASE_URL}/objects/${encodeURIComponent(object)}/records/query`,
113
- {
114
- method: "POST",
115
- headers: authHeaders(),
116
- body: JSON.stringify(body)
117
- }
118
- );
119
- await assertOk(res, "queryRecords");
120
- return await res.json();
121
- },
122
- async getRecord(object, recordId) {
123
- const res = await fetch(
124
- `${BASE_URL}/objects/${encodeURIComponent(object)}/records/${encodeURIComponent(recordId)}`,
125
- { method: "GET", headers: authHeaders() }
125
+ // ---- Meta ----
126
+ self: () => httpJson("GET", "/self", "self"),
127
+ // ---- Objects ----
128
+ listObjects: () => httpJson("GET", "/objects", "listObjects"),
129
+ getObject: (object) => httpJson("GET", `/objects/${enc(object)}`, "getObject"),
130
+ listObjectViews: (object) => httpJson(
131
+ "GET",
132
+ `/objects/${enc(object)}/views`,
133
+ "listObjectViews"
134
+ ),
135
+ // ---- Attributes ----
136
+ listAttributes: (object) => httpJson(
137
+ "GET",
138
+ `/objects/${enc(object)}/attributes`,
139
+ "listAttributes"
140
+ ),
141
+ getAttribute: (object, attribute) => httpJson(
142
+ "GET",
143
+ `/objects/${enc(object)}/attributes/${enc(attribute)}`,
144
+ "getAttribute"
145
+ ),
146
+ listSelectOptions: (object, attribute) => httpJson(
147
+ "GET",
148
+ `/objects/${enc(object)}/attributes/${enc(attribute)}/options`,
149
+ "listSelectOptions"
150
+ ),
151
+ listStatuses: (object, attribute) => httpJson(
152
+ "GET",
153
+ `/objects/${enc(object)}/attributes/${enc(attribute)}/statuses`,
154
+ "listStatuses"
155
+ ),
156
+ // ---- Records ----
157
+ queryRecords: (object, options) => httpJson(
158
+ "POST",
159
+ `/objects/${enc(object)}/records/query`,
160
+ "queryRecords",
161
+ queryBody(options)
162
+ ),
163
+ getRecord: (object, recordId) => httpJson(
164
+ "GET",
165
+ `/objects/${enc(object)}/records/${enc(recordId)}`,
166
+ "getRecord"
167
+ ),
168
+ createRecord: (object, data) => httpJson(
169
+ "POST",
170
+ `/objects/${enc(object)}/records`,
171
+ "createRecord",
172
+ { data }
173
+ ),
174
+ updateRecord: (object, recordId, data) => httpJson(
175
+ "PATCH",
176
+ `/objects/${enc(object)}/records/${enc(recordId)}`,
177
+ "updateRecord",
178
+ { data }
179
+ ),
180
+ overwriteRecord: (object, recordId, data) => httpJson(
181
+ "PUT",
182
+ `/objects/${enc(object)}/records/${enc(recordId)}`,
183
+ "overwriteRecord",
184
+ { data }
185
+ ),
186
+ deleteRecord: (object, recordId) => httpVoid(
187
+ "DELETE",
188
+ `/objects/${enc(object)}/records/${enc(recordId)}`,
189
+ "deleteRecord"
190
+ ),
191
+ assertRecord: (object, data) => httpJson(
192
+ "PUT",
193
+ `/objects/${enc(object)}/records`,
194
+ "assertRecord",
195
+ { data }
196
+ ),
197
+ listRecordEntries: (object, recordId) => httpJson(
198
+ "GET",
199
+ `/objects/${enc(object)}/records/${enc(recordId)}/entries`,
200
+ "listRecordEntries"
201
+ ),
202
+ // ---- Lists ----
203
+ listLists: () => httpJson("GET", "/lists", "listLists"),
204
+ getList: (listId) => httpJson("GET", `/lists/${enc(listId)}`, "getList"),
205
+ listListViews: (listId) => httpJson(
206
+ "GET",
207
+ `/lists/${enc(listId)}/views`,
208
+ "listListViews"
209
+ ),
210
+ queryListEntries: (listId, options) => httpJson(
211
+ "POST",
212
+ `/lists/${enc(listId)}/entries/query`,
213
+ "queryListEntries",
214
+ queryBody(options)
215
+ ),
216
+ getListEntry: (listId, entryId) => httpJson(
217
+ "GET",
218
+ `/lists/${enc(listId)}/entries/${enc(entryId)}`,
219
+ "getListEntry"
220
+ ),
221
+ createListEntry: (listId, data) => httpJson(
222
+ "POST",
223
+ `/lists/${enc(listId)}/entries`,
224
+ "createListEntry",
225
+ { data }
226
+ ),
227
+ updateListEntry: (listId, entryId, data) => httpJson(
228
+ "PATCH",
229
+ `/lists/${enc(listId)}/entries/${enc(entryId)}`,
230
+ "updateListEntry",
231
+ { data }
232
+ ),
233
+ overwriteListEntry: (listId, entryId, data) => httpJson(
234
+ "PUT",
235
+ `/lists/${enc(listId)}/entries/${enc(entryId)}`,
236
+ "overwriteListEntry",
237
+ { data }
238
+ ),
239
+ deleteListEntry: (listId, entryId) => httpVoid(
240
+ "DELETE",
241
+ `/lists/${enc(listId)}/entries/${enc(entryId)}`,
242
+ "deleteListEntry"
243
+ ),
244
+ assertListEntry: (listId, data) => httpJson(
245
+ "PUT",
246
+ `/lists/${enc(listId)}/entries`,
247
+ "assertListEntry",
248
+ { data }
249
+ ),
250
+ // ---- Workspace Members ----
251
+ listWorkspaceMembers: () => httpJson("GET", "/workspace_members", "listWorkspaceMembers"),
252
+ getWorkspaceMember: (workspaceMemberId) => httpJson(
253
+ "GET",
254
+ `/workspace_members/${enc(workspaceMemberId)}`,
255
+ "getWorkspaceMember"
256
+ ),
257
+ async getWorkspaceMemberMap() {
258
+ const { data } = await httpJson(
259
+ "GET",
260
+ "/workspace_members",
261
+ "getWorkspaceMemberMap"
126
262
  );
127
- await assertOk(res, "getRecord");
128
- return await res.json();
263
+ const map = /* @__PURE__ */ new Map();
264
+ for (const m of data) {
265
+ const id = m?.id?.workspace_member_id;
266
+ if (!id) continue;
267
+ const name = [m.first_name, m.last_name].filter(Boolean).join(" ").trim() || m.email_address || id;
268
+ map.set(id, { id, name, email: m.email_address });
269
+ }
270
+ return map;
129
271
  },
130
- async queryListEntries(listId, options) {
131
- const body = {};
132
- if (options?.filter) body.filter = options.filter;
133
- if (options?.sorts) body.sorts = options.sorts;
134
- if (options?.limit) body.limit = options.limit;
135
- if (options?.offset) body.offset = options.offset;
136
- const res = await fetch(
137
- `${BASE_URL}/lists/${encodeURIComponent(listId)}/entries/query`,
138
- {
139
- method: "POST",
140
- headers: authHeaders(),
141
- body: JSON.stringify(body)
142
- }
143
- );
144
- await assertOk(res, "queryListEntries");
145
- return await res.json();
146
- }
272
+ // ---- Notes ----
273
+ listNotes: (options) => httpJson("GET", `/notes${buildQuery(options)}`, "listNotes"),
274
+ getNote: (noteId) => httpJson("GET", `/notes/${enc(noteId)}`, "getNote"),
275
+ createNote: (data) => httpJson("POST", "/notes", "createNote", { data }),
276
+ updateNote: (noteId, data) => httpJson("PATCH", `/notes/${enc(noteId)}`, "updateNote", {
277
+ data
278
+ }),
279
+ deleteNote: (noteId) => httpVoid("DELETE", `/notes/${enc(noteId)}`, "deleteNote"),
280
+ // ---- Tasks ----
281
+ listTasks: (options) => httpJson("GET", `/tasks${buildQuery(options)}`, "listTasks"),
282
+ getTask: (taskId) => httpJson("GET", `/tasks/${enc(taskId)}`, "getTask"),
283
+ createTask: (data) => httpJson("POST", "/tasks", "createTask", { data }),
284
+ updateTask: (taskId, data) => httpJson("PATCH", `/tasks/${enc(taskId)}`, "updateTask", {
285
+ data
286
+ }),
287
+ deleteTask: (taskId) => httpVoid("DELETE", `/tasks/${enc(taskId)}`, "deleteTask"),
288
+ // ---- Threads & Comments ----
289
+ listThreads: (options) => httpJson(
290
+ "GET",
291
+ `/threads${buildQuery(options)}`,
292
+ "listThreads"
293
+ ),
294
+ getThread: (threadId) => httpJson("GET", `/threads/${enc(threadId)}`, "getThread"),
295
+ createComment: (data) => httpJson("POST", "/comments", "createComment", { data }),
296
+ getComment: (commentId) => httpJson("GET", `/comments/${enc(commentId)}`, "getComment"),
297
+ updateComment: (commentId, data) => httpJson(
298
+ "PATCH",
299
+ `/comments/${enc(commentId)}`,
300
+ "updateComment",
301
+ { data }
302
+ ),
303
+ deleteComment: (commentId) => httpVoid(
304
+ "DELETE",
305
+ `/comments/${enc(commentId)}`,
306
+ "deleteComment"
307
+ ),
308
+ // ---- Webhooks ----
309
+ listWebhooks: () => httpJson("GET", "/webhooks", "listWebhooks"),
310
+ getWebhook: (webhookId) => httpJson("GET", `/webhooks/${enc(webhookId)}`, "getWebhook"),
311
+ createWebhook: (data) => httpJson("POST", "/webhooks", "createWebhook", { data }),
312
+ updateWebhook: (webhookId, data) => httpJson(
313
+ "PATCH",
314
+ `/webhooks/${enc(webhookId)}`,
315
+ "updateWebhook",
316
+ { data }
317
+ ),
318
+ deleteWebhook: (webhookId) => httpVoid(
319
+ "DELETE",
320
+ `/webhooks/${enc(webhookId)}`,
321
+ "deleteWebhook"
322
+ ),
323
+ // ---- Meetings ----
324
+ listMeetings: (options) => httpJson(
325
+ "GET",
326
+ `/meetings${buildQuery(options)}`,
327
+ "listMeetings"
328
+ ),
329
+ getMeeting: (meetingId) => httpJson("GET", `/meetings/${enc(meetingId)}`, "getMeeting")
147
330
  };
148
331
  }
149
332
 
@@ -297,14 +480,18 @@ var AUTH_TYPES = {
297
480
  // ../connectors/src/connectors/attio/setup.ts
298
481
  var attioOnboarding = new ConnectorOnboarding({
299
482
  dataOverviewInstructions: {
300
- en: `1. Call attio_request with GET /objects to list all available objects (people, companies, deals, etc.)
301
- 2. Call attio_request with GET /objects/people/attributes to explore the people object attributes
302
- 3. Call attio_request with POST /objects/people/records/query with { "limit": 5 } to sample people records
303
- 4. Explore other objects (companies, deals) as needed`,
304
- ja: `1. attio_request \u3067 GET /objects \u3092\u547C\u3073\u51FA\u3057\u3001\u5229\u7528\u53EF\u80FD\u306A\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\uFF08people\u3001companies\u3001deals\u306A\u3069\uFF09
305
- 2. attio_request \u3067 GET /objects/people/attributes \u3092\u547C\u3073\u51FA\u3057\u3001people\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u5C5E\u6027\u3092\u78BA\u8A8D
306
- 3. attio_request \u3067 POST /objects/people/records/query \u3092 { "limit": 5 } \u3067\u547C\u3073\u51FA\u3057\u3001people\u30EC\u30B3\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0
307
- 4. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u4ED6\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\uFF08companies\u3001deals\uFF09\u3092\u63A2\u7D22`
483
+ en: `1. Call attio_request with GET /objects to discover the object slugs actually configured in this workspace (typically people, companies, deals, plus any custom objects).
484
+ 2. For each object you plan to use, call attio_request with GET /objects/{slug}/attributes to explore its attributes
485
+ 3. Call attio_request with POST /objects/{slug}/records/query with { "limit": 5 } to sample records
486
+ 4. Call attio_request with GET /workspace_members (top-level, snake_case) to list team members. The response shape is \`{ data: [{ id: { workspace_member_id }, first_name, last_name, email_address, ... }] }\`. You need this mapping in dashboard handlers to resolve \`owner\` and other actor-reference fields (which return \`referenced_actor_id\`, not a name). Sample one record that has an \`owner\` and confirm the shape \`{ referenced_actor_type, referenced_actor_id }\`, then use \`client.getWorkspaceMemberMap()\` in handlers to resolve IDs to names.
487
+ 5. If an endpoint later returns 403 (tasks / threads / webhooks / meetings require specific scopes), call GET /self to inspect the token's active scopes.
488
+ 6. Explore lists via GET /lists as needed, and use /notes, /tasks, /threads, /comments, /webhooks, /meetings when the question requires them.`,
489
+ ja: `1. attio_request \u3067 GET /objects \u3092\u547C\u3073\u51FA\u3057\u3001\u3053\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306B\u5B9F\u5728\u3059\u308B\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8slug\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u901A\u5E38\u306F people / companies / deals\u3001\u52A0\u3048\u3066\u8A2D\u5B9A\u6E08\u307F\u306E\u30AB\u30B9\u30BF\u30E0\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\uFF09
490
+ 2. \u4F7F\u3046\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3054\u3068\u306B attio_request \u3067 GET /objects/{slug}/attributes \u3092\u547C\u3073\u51FA\u3057\u3001\u5C5E\u6027\u3092\u78BA\u8A8D
491
+ 3. attio_request \u3067 POST /objects/{slug}/records/query \u3092 { "limit": 5 } \u3067\u547C\u3073\u51FA\u3057\u3001\u30EC\u30B3\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0
492
+ 4. attio_request \u3067 GET /workspace_members\uFF08\u30C8\u30C3\u30D7\u30EC\u30D9\u30EB\u3001\u30B9\u30CD\u30FC\u30AF\u30B1\u30FC\u30B9\uFF09\u3092\u547C\u3073\u51FA\u3057\u3001\u30C1\u30FC\u30E0\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u3092\u53D6\u5F97\u3002\u30EC\u30B9\u30DD\u30F3\u30B9\u306F \`{ data: [{ id: { workspace_member_id }, first_name, last_name, email_address, ... }] }\` \u306E\u5F62\u5F0F\u3067\u3059\u3002\u3053\u306E\u30DE\u30C3\u30D4\u30F3\u30B0\u306F\u3001\`owner\` \u306A\u3069\u306E actor-reference \u30D5\u30A3\u30FC\u30EB\u30C9\uFF08\`referenced_actor_id\` \u3092\u8FD4\u3059\uFF09\u3092\u540D\u524D\u306B\u89E3\u6C7A\u3059\u308B\u305F\u3081\u306B\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u30CF\u30F3\u30C9\u30E9\u3067\u5FC5\u9808\u3067\u3059\u3002\`owner\` \u3092\u6301\u3064\u30EC\u30B3\u30FC\u30C9\u30921\u4EF6\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u3057\u3001\`{ referenced_actor_type, referenced_actor_id }\` \u306E\u5F62\u3092\u78BA\u8A8D\u3057\u305F\u4E0A\u3067\u3001\u30CF\u30F3\u30C9\u30E9\u3067\u306F \`client.getWorkspaceMemberMap()\` \u3092\u4F7F\u3063\u3066 ID \u2192 \u540D\u524D \u306B\u89E3\u6C7A\u3057\u3066\u304F\u3060\u3055\u3044
493
+ 5. \u5F8C\u6BB5\u3067 403 \u304C\u51FA\u305F\u5834\u5408\uFF08tasks / threads / webhooks / meetings \u306A\u3069\u306F\u500B\u5225\u306E scope \u304C\u5FC5\u8981\uFF09\u306F\u3001GET /self \u3092\u547C\u3073\u51FA\u3057\u3066API\u30C8\u30FC\u30AF\u30F3\u306E\u6709\u52B9 scope \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044
494
+ 6. \u5FC5\u8981\u306B\u5FDC\u3058\u3066 GET /lists \u3067\u30EA\u30B9\u30C8\u3092\u63A2\u7D22\u3057\u3001/notes\u3001/tasks\u3001/threads\u3001/comments\u3001/webhooks\u3001/meetings \u3082\u6D3B\u7528\u3057\u3066\u304F\u3060\u3055\u3044`
308
495
  }
309
496
  });
310
497
 
@@ -317,11 +504,11 @@ var inputSchema = z.object({
317
504
  "Brief description of what you intend to accomplish with this tool call"
318
505
  ),
319
506
  connectionId: z.string().describe("ID of the Attio connection to use"),
320
- method: z.enum(["GET", "POST", "PATCH", "DELETE"]).describe(
321
- "HTTP method. GET for reading resources, POST for creating or querying records, PATCH for updating, DELETE for removing."
507
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe(
508
+ "HTTP method. GET for reading resources and list endpoints, POST for creating records/querying/asserting, PATCH for partial updates (append multiselect), PUT for full updates (overwrite multiselect), DELETE for removing."
322
509
  ),
323
510
  path: z.string().describe(
324
- "API path (e.g., '/objects', '/objects/people/records/query', '/objects/companies/records/{record_id}')"
511
+ "Attio REST API path relative to https://api.attio.com/v2 (include the leading slash). Examples: '/self', '/objects', '/objects/people/attributes', '/objects/people/records/query', '/objects/companies/records/{record_id}', '/workspace_members', '/workspace_members/{workspace_member_id}', '/lists', '/lists/{list_id}/entries/query', '/notes', '/tasks', '/threads', '/comments', '/webhooks', '/meetings'."
325
512
  ),
326
513
  body: z.record(z.string(), z.unknown()).optional().describe("Request body (JSON) for POST/PATCH requests")
327
514
  });
@@ -338,10 +525,10 @@ var outputSchema = z.discriminatedUnion("success", [
338
525
  ]);
339
526
  var requestTool = new ConnectorTool({
340
527
  name: "request",
341
- description: `Send authenticated requests to the Attio REST API.
528
+ description: `Send authenticated requests to the Attio REST API (base URL: https://api.attio.com/v2).
342
529
  Authentication is handled automatically using the API Key (Bearer token).
343
- Use this tool for all Attio API interactions: querying records (people, companies, deals), listing objects and attributes, managing list entries, and working with notes.
344
- Note that querying records uses POST (not GET) with a request body for filters.`,
530
+ Use this tool for every Attio API interaction, including: querying records (people, companies, deals, and any custom objects configured in the workspace) and their attributes, listing/getting/creating/updating/deleting records and list entries, reading workspace members, and working with notes, tasks, threads, comments, webhooks, and meetings. Call GET /self to introspect the current API token.
531
+ Record queries use POST /objects/{object}/records/query with a JSON body containing \`filter\`, \`sorts\`, \`limit\`, and \`offset\`. Record updates use PATCH (append multiselect) or PUT (overwrite multiselect). Workspace members, tasks, webhooks, notes, threads, comments, and meetings live at top-level paths \u2014 the workspace-members path uses a snake_case underscore: /workspace_members.`,
345
532
  inputSchema,
346
533
  outputSchema,
347
534
  async execute({ connectionId, method, path: path2, body }, connections) {
@@ -400,19 +587,69 @@ var attioConnector = new ConnectorPlugin({
400
587
  systemPrompt: {
401
588
  en: `### Tools
402
589
 
403
- - \`attio_request\`: The only way to call the Attio REST API. Use it to query records (people, companies, deals), list objects and attributes, manage list entries, and work with notes. Authentication (Bearer token) is configured automatically. Note that querying records uses POST (not GET) with a request body containing filters.
590
+ - \`attio_request\`: The only way to call the Attio REST API. Use it for every Attio resource \u2014 records (people, companies, deals, and any custom objects configured in the workspace), their attributes, lists and entries, workspace members, notes, tasks, threads, comments, webhooks, and meetings. Authentication (Bearer token) is configured automatically. Querying records uses POST \`/objects/{object}/records/query\` with a JSON body. Use PATCH for partial updates (append multiselect) and PUT for full updates (overwrite multiselect). Always call GET \`/objects\` first to discover the actual object slugs in this workspace.
404
591
 
405
592
  ### Business Logic
406
593
 
407
- The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
594
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler; authentication is handled by the SDK client.
408
595
 
409
596
  SDK methods (client created via \`connection(connectionId)\`):
410
597
  - \`client.request(path, init?)\` \u2014 low-level authenticated fetch
411
- - \`client.listObjects()\` \u2014 list all objects (people, companies, deals, etc.)
412
- - \`client.listAttributes(object)\` \u2014 list attributes for an object
413
- - \`client.queryRecords(object, options?)\` \u2014 query records with filter/sort/pagination
414
- - \`client.getRecord(object, recordId)\` \u2014 fetch a single record
415
- - \`client.queryListEntries(listId, options?)\` \u2014 query list entries with filter/sort/pagination
598
+ - \`client.self()\` \u2014 current API token introspection (GET /self) \u2014 returns workspace id, scopes, token status
599
+ - Objects: \`client.listObjects()\`, \`client.getObject(object)\`, \`client.listObjectViews(object)\`
600
+ - Attributes: \`client.listAttributes(object)\`, \`client.getAttribute(object, attribute)\`, \`client.listSelectOptions(object, attribute)\`, \`client.listStatuses(object, attribute)\`
601
+ - Records: \`client.queryRecords(object, options?)\`, \`client.getRecord(object, recordId)\`, \`client.createRecord(object, data)\`, \`client.updateRecord(object, recordId, data)\` (PATCH), \`client.overwriteRecord(object, recordId, data)\` (PUT), \`client.deleteRecord(object, recordId)\`, \`client.assertRecord(object, data)\`, \`client.listRecordEntries(object, recordId)\`
602
+ - Lists & entries: \`client.listLists()\`, \`client.getList(listId)\`, \`client.listListViews(listId)\`, \`client.queryListEntries(listId, options?)\`, \`client.getListEntry(listId, entryId)\`, \`client.createListEntry(listId, data)\`, \`client.updateListEntry(listId, entryId, data)\`, \`client.overwriteListEntry(listId, entryId, data)\`, \`client.deleteListEntry(listId, entryId)\`, \`client.assertListEntry(listId, data)\`
603
+ - Workspace members: \`client.listWorkspaceMembers()\`, \`client.getWorkspaceMember(workspaceMemberId)\`, \`client.getWorkspaceMemberMap()\` (returns \`Map<workspace_member_id, { id, name, email }>\` \u2014 use to resolve \`owner\` / actor-reference fields to names)
604
+ - Notes: \`client.listNotes(options?)\`, \`client.getNote(noteId)\`, \`client.createNote(data)\`, \`client.updateNote(noteId, data)\`, \`client.deleteNote(noteId)\`
605
+ - Tasks: \`client.listTasks(options?)\`, \`client.getTask(taskId)\`, \`client.createTask(data)\`, \`client.updateTask(taskId, data)\`, \`client.deleteTask(taskId)\`
606
+ - Threads & comments: \`client.listThreads(options?)\`, \`client.getThread(threadId)\`, \`client.createComment(data)\`, \`client.getComment(commentId)\`, \`client.updateComment(commentId, data)\`, \`client.deleteComment(commentId)\`
607
+ - Webhooks: \`client.listWebhooks()\`, \`client.getWebhook(webhookId)\`, \`client.createWebhook(data)\`, \`client.updateWebhook(webhookId, data)\`, \`client.deleteWebhook(webhookId)\`
608
+ - Meetings: \`client.listMeetings(options?)\`, \`client.getMeeting(meetingId)\`
609
+
610
+ ### CRITICAL: Owner / Actor reference resolution
611
+
612
+ Fields like \`owner\` \u2014 and any \`actor-reference\` attribute \u2014 in Attio record \`values\` do **not** contain names. They carry actor IDs shaped like:
613
+ \`\`\`json
614
+ { "referenced_actor_type": "workspace-member", "referenced_actor_id": "<workspace_member_id>" }
615
+ \`\`\`
616
+ Rendering that raw leaves opaque IDs in the UI (e.g. \`owner_name\` stays as a UUID). **Always** resolve these IDs to names before returning them. Do this by fetching workspace members once and joining on \`workspace_member_id\`.
617
+
618
+ - Use \`client.getWorkspaceMemberMap()\` \u2014 it returns a typed \`Map<workspace_member_id, { id, name, email }>\` ready to join with actor references.
619
+ - The workspace-member endpoint is top-level: \`GET /workspace_members\`.
620
+ - Fetch \`client.getWorkspaceMemberMap()\` in parallel with \`queryRecords\` via \`Promise.all\` (single fetch, joined in-memory).
621
+
622
+ Example \u2014 owner-based performance with names resolved:
623
+ \`\`\`ts
624
+ import type { Context } from "hono";
625
+ import { connection } from "@squadbase/vite-server/connectors/attio";
626
+
627
+ const attio = connection("<connectionId>");
628
+
629
+ export default async function handler(c: Context) {
630
+ const [{ data: deals }, memberMap] = await Promise.all([
631
+ attio.queryRecords("deals", { limit: 500 }),
632
+ attio.getWorkspaceMemberMap(),
633
+ ]);
634
+
635
+ const byOwner = new Map<string, { owner_name: string; count: number }>();
636
+ for (const deal of deals) {
637
+ const values = (deal as { values?: Record<string, unknown[]> }).values ?? {};
638
+ const ownerCell = (values.owner ?? [])[0] as
639
+ | { referenced_actor_id?: string }
640
+ | undefined;
641
+ const ownerId = ownerCell?.referenced_actor_id ?? "unassigned";
642
+ const ownerName = memberMap.get(ownerId)?.name ?? "Unassigned";
643
+ const prev = byOwner.get(ownerId) ?? { owner_name: ownerName, count: 0 };
644
+ prev.count += 1;
645
+ byOwner.set(ownerId, prev);
646
+ }
647
+
648
+ return c.json({ rows: Array.from(byOwner.values()) });
649
+ }
650
+ \`\`\`
651
+
652
+ ### Minimal example
416
653
 
417
654
  \`\`\`ts
418
655
  import type { Context } from "hono";
@@ -437,39 +674,129 @@ export default async function handler(c: Context) {
437
674
  - Base URL: \`https://api.attio.com/v2\`
438
675
  - Authentication: Bearer token (handled automatically)
439
676
  - Querying records uses POST with JSON body (not GET with query params)
440
-
441
- #### Common Endpoints
442
- - GET \`/objects\` \u2014 List all objects
443
- - GET \`/objects/{object}/attributes\` \u2014 List attributes of an object
444
- - POST \`/objects/{object}/records/query\` \u2014 Query records (people, companies, deals, etc.)
445
- - GET \`/objects/{object}/records/{record_id}\` \u2014 Get a record
446
- - POST \`/objects/{object}/records\` \u2014 Create a record
447
- - PATCH \`/objects/{object}/records/{record_id}\` \u2014 Update a record
448
- - DELETE \`/objects/{object}/records/{record_id}\` \u2014 Delete a record
449
- - POST \`/lists/{list_id}/entries/query\` \u2014 Query list entries
450
- - POST \`/notes\` \u2014 Create a note
451
- - GET \`/notes?parent_object={object}&parent_record_id={id}\` \u2014 Get notes for a record
452
-
453
- #### Query Body (for POST /objects/{object}/records/query)
454
- - \`filter\` \u2014 Filter conditions
455
- - \`sorts\` \u2014 Array of sort specifications
456
- - \`limit\` \u2014 Max records per page (default 25)
457
- - \`offset\` \u2014 Pagination offset`,
677
+ - Update semantics: PATCH appends multiselect values, PUT overwrites them
678
+ - Many list endpoints support \`limit\` and \`offset\` for pagination
679
+
680
+ #### Meta
681
+ - GET \`/self\` \u2014 current API token introspection (workspace id, scopes, token status)
682
+
683
+ #### Objects & Attributes
684
+ - GET \`/objects\` \u2014 list all objects in the workspace (default: people, companies, deals; plus any custom objects configured)
685
+ - GET \`/objects/{object}\` \u2014 get a single object
686
+ - GET \`/objects/{object}/views\` \u2014 list views for an object
687
+ - GET \`/objects/{object}/attributes\` \u2014 list attributes for an object
688
+ - GET \`/objects/{object}/attributes/{attribute}\` \u2014 get an attribute
689
+ - GET \`/objects/{object}/attributes/{attribute}/options\` \u2014 list select options
690
+ - GET \`/objects/{object}/attributes/{attribute}/statuses\` \u2014 list statuses
691
+
692
+ #### Records
693
+ - POST \`/objects/{object}/records/query\` \u2014 query records (filter / sorts / limit / offset)
694
+ - GET \`/objects/{object}/records/{record_id}\` \u2014 get a record
695
+ - POST \`/objects/{object}/records\` \u2014 create a record (body: \`{ "data": { ... } }\`)
696
+ - PATCH \`/objects/{object}/records/{record_id}\` \u2014 update (append multiselect)
697
+ - PUT \`/objects/{object}/records/{record_id}\` \u2014 update (overwrite multiselect)
698
+ - DELETE \`/objects/{object}/records/{record_id}\` \u2014 delete a record
699
+ - PUT \`/objects/{object}/records\` \u2014 assert (upsert) by matching attribute
700
+ - GET \`/objects/{object}/records/{record_id}/entries\` \u2014 list list entries that reference the record
701
+
702
+ #### Lists & Entries
703
+ - GET \`/lists\` \u2014 list all lists
704
+ - GET \`/lists/{list_id}\` \u2014 get a list
705
+ - GET \`/lists/{list_id}/views\` \u2014 list views for a list
706
+ - POST \`/lists/{list_id}/entries/query\` \u2014 query list entries
707
+ - GET \`/lists/{list_id}/entries/{entry_id}\` \u2014 get a list entry
708
+ - POST \`/lists/{list_id}/entries\` \u2014 create an entry
709
+ - PATCH \`/lists/{list_id}/entries/{entry_id}\` \u2014 update (append multiselect)
710
+ - PUT \`/lists/{list_id}/entries/{entry_id}\` \u2014 update (overwrite multiselect)
711
+ - DELETE \`/lists/{list_id}/entries/{entry_id}\` \u2014 delete an entry
712
+ - PUT \`/lists/{list_id}/entries\` \u2014 assert (upsert) a list entry by parent
713
+
714
+ #### Workspace Members
715
+ - GET \`/workspace_members\` \u2014 list every member of the workspace (team members, their roles, and access levels)
716
+ - GET \`/workspace_members/{workspace_member_id}\` \u2014 get a single workspace member
717
+
718
+ #### Notes, Tasks, Threads & Comments
719
+ - \`/notes\` (GET list, POST create) and \`/notes/{note_id}\` (GET / PATCH / DELETE). Filter list notes with \`?parent_object={object}&parent_record_id={id}\`.
720
+ - \`/tasks\` (GET list, POST create) and \`/tasks/{task_id}\` (GET / PATCH / DELETE)
721
+ - \`/threads\` (GET list) and \`/threads/{thread_id}\` (GET)
722
+ - \`/comments\` (POST create) and \`/comments/{comment_id}\` (GET / PATCH / DELETE)
723
+
724
+ #### Webhooks
725
+ - GET \`/webhooks\`, POST \`/webhooks\`, GET/PATCH/DELETE \`/webhooks/{webhook_id}\`
726
+
727
+ #### Meetings
728
+ - GET \`/meetings\`, GET \`/meetings/{meeting_id}\`
729
+
730
+ #### Query Body (for POST /objects/{object}/records/query and /lists/{list_id}/entries/query)
731
+ - \`filter\` \u2014 filter conditions
732
+ - \`sorts\` \u2014 array of sort specifications
733
+ - \`limit\` \u2014 max records per page (default 25)
734
+ - \`offset\` \u2014 pagination offset`,
458
735
  ja: `### \u30C4\u30FC\u30EB
459
736
 
460
- - \`attio_request\`: Attio REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30EC\u30B3\u30FC\u30C9\uFF08people\u3001companies\u3001deals\uFF09\u306E\u30AF\u30A8\u30EA\u3001\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3084\u5C5E\u6027\u306E\u4E00\u89A7\u8868\u793A\u3001\u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u306E\u7BA1\u7406\u3001\u30CE\u30FC\u30C8\u306E\u64CD\u4F5C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08Bearer\u30C8\u30FC\u30AF\u30F3\uFF09\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u30EC\u30B3\u30FC\u30C9\u306E\u30AF\u30A8\u30EA\u306B\u306FGET\u3067\u306F\u306A\u304FPOST\u3092\u30D5\u30A3\u30EB\u30BF\u4ED8\u304D\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u3067\u4F7F\u7528\u3059\u308B\u70B9\u306B\u6CE8\u610F\u3057\u3066\u304F\u3060\u3055\u3044\u3002
737
+ - \`attio_request\`: Attio REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30EC\u30B3\u30FC\u30C9\uFF08people\u3001companies\u3001deals\u3001\u304A\u3088\u3073\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306B\u8A2D\u5B9A\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30E0\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\uFF09\u3068\u305D\u306E\u5C5E\u6027\u3001\u30EA\u30B9\u30C8\u3068\u30A8\u30F3\u30C8\u30EA\u3001\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u30E1\u30F3\u30D0\u30FC\u3001\u30CE\u30FC\u30C8\u3001\u30BF\u30B9\u30AF\u3001\u30B9\u30EC\u30C3\u30C9\u3001\u30B3\u30E1\u30F3\u30C8\u3001Webhook\u3001\u30DF\u30FC\u30C6\u30A3\u30F3\u30B0\u306A\u3069\u3001\u3059\u3079\u3066\u306EAttio\u30EA\u30BD\u30FC\u30B9\u306E\u64CD\u4F5C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08Bearer\u30C8\u30FC\u30AF\u30F3\uFF09\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u30EC\u30B3\u30FC\u30C9\u306E\u30AF\u30A8\u30EA\u306B\u306F POST \`/objects/{object}/records/query\` \u3092\u30D5\u30A3\u30EB\u30BF\u4ED8\u304D\u306EJSON\u30DC\u30C7\u30A3\u3067\u4F7F\u7528\u3057\u307E\u3059\u3002\u90E8\u5206\u66F4\u65B0\uFF08multiselect\u306E\u8FFD\u52A0\uFF09\u306B\u306FPATCH\u3001\u5168\u7F6E\u63DB\uFF08multiselect\u306E\u4E0A\u66F8\u304D\uFF09\u306B\u306FPUT\u3092\u4F7F\u3044\u307E\u3059\u3002**\u5FC5\u305A** \u6700\u521D\u306B GET \`/objects\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u3053\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306B\u5B9F\u5728\u3059\u308B\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8slug\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
461
738
 
462
739
  ### Business Logic
463
740
 
464
- \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
741
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u8A8D\u8A3C\u306FSDK\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u5074\u3067\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
465
742
 
466
743
  SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
467
744
  - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
468
- - \`client.listObjects()\` \u2014 \u5168\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u4E00\u89A7\uFF08people\u3001companies\u3001deals\u306A\u3069\uFF09
469
- - \`client.listAttributes(object)\` \u2014 \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u5C5E\u6027\u4E00\u89A7
470
- - \`client.queryRecords(object, options?)\` \u2014 \u30D5\u30A3\u30EB\u30BF/\u30BD\u30FC\u30C8/\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u4ED8\u304D\u3067\u30EC\u30B3\u30FC\u30C9\u3092\u30AF\u30A8\u30EA
471
- - \`client.getRecord(object, recordId)\` \u2014 \u5358\u4E00\u30EC\u30B3\u30FC\u30C9\u3092\u53D6\u5F97
472
- - \`client.queryListEntries(listId, options?)\` \u2014 \u30D5\u30A3\u30EB\u30BF/\u30BD\u30FC\u30C8/\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u4ED8\u304D\u3067\u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u3092\u30AF\u30A8\u30EA
745
+ - \`client.identify()\` \u2014 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9/\u30C8\u30FC\u30AF\u30F3\u60C5\u5831\uFF08GET /meta/identify\uFF09
746
+ - Objects: \`client.listObjects()\`, \`client.getObject(object)\`, \`client.listObjectViews(object)\`
747
+ - Attributes: \`client.listAttributes(object)\`, \`client.getAttribute(object, attribute)\`, \`client.listSelectOptions(object, attribute)\`, \`client.listStatuses(object, attribute)\`
748
+ - Records: \`client.queryRecords(object, options?)\`, \`client.getRecord(object, recordId)\`, \`client.createRecord(object, data)\`, \`client.updateRecord(object, recordId, data)\` (PATCH), \`client.overwriteRecord(object, recordId, data)\` (PUT), \`client.deleteRecord(object, recordId)\`, \`client.assertRecord(object, data)\`, \`client.listRecordEntries(object, recordId)\`
749
+ - Lists & entries: \`client.listLists()\`, \`client.getList(listId)\`, \`client.listListViews(listId)\`, \`client.queryListEntries(listId, options?)\`, \`client.getListEntry(listId, entryId)\`, \`client.createListEntry(listId, data)\`, \`client.updateListEntry(listId, entryId, data)\`, \`client.overwriteListEntry(listId, entryId, data)\`, \`client.deleteListEntry(listId, entryId)\`, \`client.assertListEntry(listId, data)\`
750
+ - Workspace members: \`client.listWorkspaceMembers()\`, \`client.getWorkspaceMember(workspaceMemberId)\`, \`client.getWorkspaceMemberMap()\` (returns \`Map<workspace_member_id, { id, name, email }>\` \u2014 use to resolve \`owner\` / actor-reference fields to names)
751
+ - Notes: \`client.listNotes(options?)\`, \`client.getNote(noteId)\`, \`client.createNote(data)\`, \`client.updateNote(noteId, data)\`, \`client.deleteNote(noteId)\`
752
+ - Tasks: \`client.listTasks(options?)\`, \`client.getTask(taskId)\`, \`client.createTask(data)\`, \`client.updateTask(taskId, data)\`, \`client.deleteTask(taskId)\`
753
+ - Threads & comments: \`client.listThreads(options?)\`, \`client.getThread(threadId)\`, \`client.createComment(data)\`, \`client.getComment(commentId)\`, \`client.updateComment(commentId, data)\`, \`client.deleteComment(commentId)\`
754
+ - Webhooks: \`client.listWebhooks()\`, \`client.getWebhook(webhookId)\`, \`client.createWebhook(data)\`, \`client.updateWebhook(webhookId, data)\`, \`client.deleteWebhook(webhookId)\`
755
+ - Meetings: \`client.listMeetings(options?)\`, \`client.getMeeting(meetingId)\`
756
+
757
+ ### \u91CD\u8981: Owner / Actor reference \u306E\u540D\u524D\u89E3\u6C7A
758
+
759
+ \u30EC\u30B3\u30FC\u30C9\u5024\u306E \`owner\`\uFF08\u304A\u3088\u3073 \`actor-reference\` \u578B\u306E\u5C5E\u6027\uFF09\u306F\u540D\u524D\u3067\u306F\u306A\u304F\u3001\u6B21\u306E\u3088\u3046\u306Aactor ID\u3092\u542B\u307F\u307E\u3059\uFF1A
760
+ \`\`\`json
761
+ { "referenced_actor_type": "workspace-member", "referenced_actor_id": "<workspace_member_id>" }
762
+ \`\`\`
763
+ \u3053\u308C\u3092\u305D\u306E\u307E\u307E\u8FD4\u3059\u3068UI\u306B\u4E0D\u900F\u660E\u306AID\uFF08\`owner_name\` \u304CUUID\u306E\u307E\u307E\u7B49\uFF09\u304C\u8868\u793A\u3055\u308C\u307E\u3059\u3002**\u5FC5\u305A** \u30CF\u30F3\u30C9\u30E9\u5185\u3067 \`workspace_member_id\` \u2192 \u540D\u524D\u3078\u89E3\u6C7A\u3057\u3066\u304B\u3089\u8FD4\u3057\u3066\u304F\u3060\u3055\u3044\u3002
764
+
765
+ - \u89E3\u6C7A\u306B\u306F \`client.getWorkspaceMemberMap()\` \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002\u3053\u308C\u306F \`Map<workspace_member_id, { id, name, email }>\` \u3092\u8FD4\u3057\u3001\u305D\u306E\u307E\u307Eactor reference\u3068\u7D50\u5408\u3067\u304D\u307E\u3059\u3002
766
+ - \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u30E1\u30F3\u30D0\u30FC\u306E\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306F\u30C8\u30C3\u30D7\u30EC\u30D9\u30EB\u306E \`GET /workspace_members\` \u3067\u3059\u3002
767
+ - \`queryRecords\` \u3068 \`getWorkspaceMemberMap\` \u306F \`Promise.all\` \u3067\u4E26\u5217\u53D6\u5F97\u3057\u3001\u30E1\u30E2\u30EA\u4E0A\u3067\u7D50\u5408\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u90FD\u5EA6\u53D6\u5F97\u305B\u305A\u4E00\u62EC\u53D6\u5F97\uFF09\u3002
768
+
769
+ \u4F8B \u2014 owner\u3054\u3068\u306E\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\uFF08\u540D\u524D\u89E3\u6C7A\u8FBC\u307F\uFF09:
770
+ \`\`\`ts
771
+ import type { Context } from "hono";
772
+ import { connection } from "@squadbase/vite-server/connectors/attio";
773
+
774
+ const attio = connection("<connectionId>");
775
+
776
+ export default async function handler(c: Context) {
777
+ const [{ data: deals }, memberMap] = await Promise.all([
778
+ attio.queryRecords("deals", { limit: 500 }),
779
+ attio.getWorkspaceMemberMap(),
780
+ ]);
781
+
782
+ const byOwner = new Map<string, { owner_name: string; count: number }>();
783
+ for (const deal of deals) {
784
+ const values = (deal as { values?: Record<string, unknown[]> }).values ?? {};
785
+ const ownerCell = (values.owner ?? [])[0] as
786
+ | { referenced_actor_id?: string }
787
+ | undefined;
788
+ const ownerId = ownerCell?.referenced_actor_id ?? "unassigned";
789
+ const ownerName = memberMap.get(ownerId)?.name ?? "\u672A\u5272\u5F53";
790
+ const prev = byOwner.get(ownerId) ?? { owner_name: ownerName, count: 0 };
791
+ prev.count += 1;
792
+ byOwner.set(ownerId, prev);
793
+ }
794
+
795
+ return c.json({ rows: Array.from(byOwner.values()) });
796
+ }
797
+ \`\`\`
798
+
799
+ ### \u6700\u5C0F\u30B5\u30F3\u30D7\u30EB
473
800
 
474
801
  \`\`\`ts
475
802
  import type { Context } from "hono";
@@ -494,20 +821,60 @@ export default async function handler(c: Context) {
494
821
  - \u30D9\u30FC\u30B9URL: \`https://api.attio.com/v2\`
495
822
  - \u8A8D\u8A3C: Bearer\u30C8\u30FC\u30AF\u30F3\uFF08\u81EA\u52D5\u8A2D\u5B9A\uFF09
496
823
  - \u30EC\u30B3\u30FC\u30C9\u306E\u30AF\u30A8\u30EA\u306B\u306FGET\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u306F\u306A\u304FPOST\u3067JSON\u30DC\u30C7\u30A3\u3092\u4F7F\u7528\u3057\u307E\u3059
824
+ - \u66F4\u65B0\u30BB\u30DE\u30F3\u30C6\u30A3\u30AF\u30B9: PATCH\u306Fmultiselect\u5024\u3092\u8FFD\u52A0\u3001PUT\u306F\u4E0A\u66F8\u304D
825
+ - \u591A\u304F\u306E\u4E00\u89A7\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306F \`limit\` \u3068 \`offset\` \u306B\u3088\u308B\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u3092\u30B5\u30DD\u30FC\u30C8
826
+
827
+ #### Meta
828
+ - GET \`/self\` \u2014 \u73FE\u5728\u306EAPI\u30C8\u30FC\u30AF\u30F3\u306E\u60C5\u5831\uFF08workspace id / scopes / \u6709\u52B9\u72B6\u614B\uFF09
497
829
 
498
- #### \u4E3B\u8981\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
499
- - GET \`/objects\` \u2014 \u5168\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u4E00\u89A7
830
+ #### Objects & Attributes
831
+ - GET \`/objects\` \u2014 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306E\u5168\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306F people / companies / deals\u3001\u52A0\u3048\u3066\u8A2D\u5B9A\u6E08\u307F\u306E\u30AB\u30B9\u30BF\u30E0\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\uFF09
832
+ - GET \`/objects/{object}\` \u2014 \u5358\u4E00\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u53D6\u5F97
833
+ - GET \`/objects/{object}/views\` \u2014 \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u30D3\u30E5\u30FC\u4E00\u89A7
500
834
  - GET \`/objects/{object}/attributes\` \u2014 \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u5C5E\u6027\u4E00\u89A7
501
- - POST \`/objects/{object}/records/query\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u691C\u7D22\uFF08people\u3001companies\u3001deals\u306A\u3069\uFF09
835
+ - GET \`/objects/{object}/attributes/{attribute}\` \u2014 \u5C5E\u6027\u306E\u53D6\u5F97
836
+ - GET \`/objects/{object}/attributes/{attribute}/options\` \u2014 \u30BB\u30EC\u30AF\u30C8\u9078\u629E\u80A2\u306E\u4E00\u89A7
837
+ - GET \`/objects/{object}/attributes/{attribute}/statuses\` \u2014 \u30B9\u30C6\u30FC\u30BF\u30B9\u4E00\u89A7
838
+
839
+ #### Records
840
+ - POST \`/objects/{object}/records/query\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u691C\u7D22\uFF08filter / sorts / limit / offset\uFF09
502
841
  - GET \`/objects/{object}/records/{record_id}\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u53D6\u5F97
503
- - POST \`/objects/{object}/records\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u4F5C\u6210
504
- - PATCH \`/objects/{object}/records/{record_id}\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u66F4\u65B0
842
+ - POST \`/objects/{object}/records\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u4F5C\u6210\uFF08body: \`{ "data": { ... } }\`\uFF09
843
+ - PATCH \`/objects/{object}/records/{record_id}\` \u2014 \u66F4\u65B0\uFF08multiselect\u8FFD\u52A0\uFF09
844
+ - PUT \`/objects/{object}/records/{record_id}\` \u2014 \u66F4\u65B0\uFF08multiselect\u4E0A\u66F8\u304D\uFF09
505
845
  - DELETE \`/objects/{object}/records/{record_id}\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u524A\u9664
506
- - POST \`/lists/{list_id}/entries/query\` \u2014 \u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u306E\u691C\u7D22
507
- - POST \`/notes\` \u2014 \u30CE\u30FC\u30C8\u306E\u4F5C\u6210
508
- - GET \`/notes?parent_object={object}&parent_record_id={id}\` \u2014 \u30EC\u30B3\u30FC\u30C9\u306E\u30CE\u30FC\u30C8\u3092\u53D6\u5F97
846
+ - PUT \`/objects/{object}/records\` \u2014 \u4E00\u81F4\u5C5E\u6027\u306B\u3088\u308B\u30A2\u30B5\u30FC\u30C8\uFF08upsert\uFF09
847
+ - GET \`/objects/{object}/records/{record_id}/entries\` \u2014 \u30EC\u30B3\u30FC\u30C9\u3092\u53C2\u7167\u3059\u308B\u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u4E00\u89A7
509
848
 
510
- #### \u30AF\u30A8\u30EA\u30DC\u30C7\u30A3 (POST /objects/{object}/records/query)
849
+ #### Lists & Entries
850
+ - GET \`/lists\` \u2014 \u30EA\u30B9\u30C8\u4E00\u89A7
851
+ - GET \`/lists/{list_id}\` \u2014 \u30EA\u30B9\u30C8\u306E\u53D6\u5F97
852
+ - GET \`/lists/{list_id}/views\` \u2014 \u30EA\u30B9\u30C8\u306E\u30D3\u30E5\u30FC\u4E00\u89A7
853
+ - POST \`/lists/{list_id}/entries/query\` \u2014 \u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u306E\u691C\u7D22
854
+ - GET \`/lists/{list_id}/entries/{entry_id}\` \u2014 \u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u306E\u53D6\u5F97
855
+ - POST \`/lists/{list_id}/entries\` \u2014 \u30A8\u30F3\u30C8\u30EA\u4F5C\u6210
856
+ - PATCH \`/lists/{list_id}/entries/{entry_id}\` \u2014 \u66F4\u65B0\uFF08multiselect\u8FFD\u52A0\uFF09
857
+ - PUT \`/lists/{list_id}/entries/{entry_id}\` \u2014 \u66F4\u65B0\uFF08multiselect\u4E0A\u66F8\u304D\uFF09
858
+ - DELETE \`/lists/{list_id}/entries/{entry_id}\` \u2014 \u30A8\u30F3\u30C8\u30EA\u524A\u9664
859
+ - PUT \`/lists/{list_id}/entries\` \u2014 \u89AA\u30EC\u30B3\u30FC\u30C9\u306B\u3088\u308B\u30A2\u30B5\u30FC\u30C8\uFF08upsert\uFF09
860
+
861
+ #### Workspace Members\uFF08\u30C1\u30FC\u30E0\u30E1\u30F3\u30D0\u30FC\uFF09
862
+ - GET \`/workspace_members\` \u2014 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306E\u5168\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\uFF08\u30ED\u30FC\u30EB\u3084\u30A2\u30AF\u30BB\u30B9\u30EC\u30D9\u30EB\u3092\u542B\u3080\uFF09
863
+ - GET \`/workspace_members/{workspace_member_id}\` \u2014 \u5358\u4E00\u30E1\u30F3\u30D0\u30FC\u306E\u53D6\u5F97
864
+
865
+ #### Notes, Tasks, Threads & Comments
866
+ - \`/notes\`\uFF08GET \u4E00\u89A7 / POST \u4F5C\u6210\uFF09\u3001\`/notes/{note_id}\`\uFF08GET / PATCH / DELETE\uFF09\u3002\u4E00\u89A7\u306F \`?parent_object={object}&parent_record_id={id}\` \u3067\u7D5E\u308A\u8FBC\u307F\u53EF\u80FD
867
+ - \`/tasks\`\uFF08GET \u4E00\u89A7 / POST \u4F5C\u6210\uFF09\u3001\`/tasks/{task_id}\`\uFF08GET / PATCH / DELETE\uFF09
868
+ - \`/threads\`\uFF08GET \u4E00\u89A7\uFF09\u3001\`/threads/{thread_id}\`\uFF08GET\uFF09
869
+ - \`/comments\`\uFF08POST \u4F5C\u6210\uFF09\u3001\`/comments/{comment_id}\`\uFF08GET / PATCH / DELETE\uFF09
870
+
871
+ #### Webhooks
872
+ - GET \`/webhooks\`\u3001POST \`/webhooks\`\u3001GET/PATCH/DELETE \`/webhooks/{webhook_id}\`
873
+
874
+ #### Meetings
875
+ - GET \`/meetings\`\u3001GET \`/meetings/{meeting_id}\`
876
+
877
+ #### \u30AF\u30A8\u30EA\u30DC\u30C7\u30A3 (POST /objects/{object}/records/query \u304A\u3088\u3073 /lists/{list_id}/entries/query)
511
878
  - \`filter\` \u2014 \u30D5\u30A3\u30EB\u30BF\u6761\u4EF6
512
879
  - \`sorts\` \u2014 \u30BD\u30FC\u30C8\u6307\u5B9A\u306E\u914D\u5217
513
880
  - \`limit\` \u2014 \u30DA\u30FC\u30B8\u3042\u305F\u308A\u306E\u6700\u5927\u30EC\u30B3\u30FC\u30C9\u6570\uFF08\u30C7\u30D5\u30A9\u30EB\u30C825\uFF09