me3-protocol 2.0.0 → 2.2.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.
- package/README.md +14 -13
- package/dist/index.d.ts +65 -0
- package/dist/index.js +204 -0
- package/examples/full.json +29 -13
- package/package.json +1 -1
- package/schema.json +130 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ It treats your online identity as a **"Digital Business Card"**—a single JSON
|
|
|
6
6
|
|
|
7
7
|
## Why it exists (brief)
|
|
8
8
|
|
|
9
|
-
People increasingly ask AI to
|
|
9
|
+
People increasingly ask AI to _find_ a person (to hire, collaborate with, or learn from). Scraping arbitrary personal sites is a failure mode for agents: it’s brittle, slow, and ambiguous. `me.json` is a small, predictable identity endpoint that makes discovery and summarization reliable.
|
|
10
10
|
|
|
11
11
|
## 1. The Specification
|
|
12
12
|
|
|
@@ -36,18 +36,19 @@ Your `me.json` defines who you are and where to find you. It is strictly typed t
|
|
|
36
36
|
|
|
37
37
|
### Core Structure (`Me3Profile`)
|
|
38
38
|
|
|
39
|
-
| Field
|
|
40
|
-
|
|
|
41
|
-
| `version`
|
|
42
|
-
| `name`
|
|
43
|
-
| `handle`
|
|
44
|
-
| `bio`
|
|
45
|
-
| `avatar`
|
|
46
|
-
| `banner`
|
|
47
|
-
| `location
|
|
48
|
-
| `links`
|
|
49
|
-
| `buttons`
|
|
50
|
-
| `pages`
|
|
39
|
+
| Field | Type | Required | Description |
|
|
40
|
+
| :--------- | :------- | :------- | :------------------------------------------------ |
|
|
41
|
+
| `version` | `string` | **Yes** | Protocol version (currently "0.1"). |
|
|
42
|
+
| `name` | `string` | **Yes** | Your display name. |
|
|
43
|
+
| `handle` | `string` | No | Your preferred username/handle. |
|
|
44
|
+
| `bio` | `string` | No | Short bio (max 500 chars). |
|
|
45
|
+
| `avatar` | `string` | No | URL to your profile picture. |
|
|
46
|
+
| `banner` | `string` | No | URL to a header/banner image. |
|
|
47
|
+
| `location` | `string` | No | Freeform location string (e.g. "Remote"). |
|
|
48
|
+
| `links` | `object` | No | Social links (website, github, twitter, etc.). |
|
|
49
|
+
| `buttons` | `array` | No | Primary actions (e.g., "Book Call", "Subscribe"). |
|
|
50
|
+
| `pages` | `array` | No | Custom content pages. |
|
|
51
|
+
| `footer` | `object` | No | Custom footer config (or `false` to hide). |
|
|
51
52
|
|
|
52
53
|
### Examples
|
|
53
54
|
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,60 @@ export interface Me3Button {
|
|
|
35
35
|
/** Optional icon (emoji or icon identifier) */
|
|
36
36
|
icon?: string;
|
|
37
37
|
}
|
|
38
|
+
export interface Me3FooterLink {
|
|
39
|
+
/** Link text */
|
|
40
|
+
text: string;
|
|
41
|
+
/** URL to open when clicked */
|
|
42
|
+
url: string;
|
|
43
|
+
}
|
|
44
|
+
export interface Me3Footer {
|
|
45
|
+
/** Custom footer text (e.g. "Built by Jane") */
|
|
46
|
+
text?: string;
|
|
47
|
+
/** Optional custom footer link */
|
|
48
|
+
link?: Me3FooterLink;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Newsletter subscription intent.
|
|
52
|
+
* When enabled, the site accepts email subscriptions via POST /api/subscribe
|
|
53
|
+
*/
|
|
54
|
+
export interface Me3IntentSubscribe {
|
|
55
|
+
/** Whether newsletter signups are enabled */
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
/** Newsletter title (e.g., "AI Weekly") - for agents to present context */
|
|
58
|
+
title?: string;
|
|
59
|
+
/** What subscribers will receive - for agents to explain the value */
|
|
60
|
+
description?: string;
|
|
61
|
+
/** How often subscribers will hear from you */
|
|
62
|
+
frequency?: "daily" | "weekly" | "monthly" | "irregular";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Booking/scheduling intent.
|
|
66
|
+
* Declares that the person accepts meeting bookings.
|
|
67
|
+
*/
|
|
68
|
+
export interface Me3IntentBook {
|
|
69
|
+
/** Whether booking is enabled */
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
/** Meeting title (e.g., "30-min Consultation") */
|
|
72
|
+
title?: string;
|
|
73
|
+
/** What the meeting is about */
|
|
74
|
+
description?: string;
|
|
75
|
+
/** Meeting duration in minutes */
|
|
76
|
+
duration?: number;
|
|
77
|
+
/** Booking provider (e.g., "cal.com", "calendly") */
|
|
78
|
+
provider?: string;
|
|
79
|
+
/** Direct booking URL */
|
|
80
|
+
url: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Intents object - declares what actions visitors/agents can take.
|
|
84
|
+
* This is the machine-readable API contract for interacting with a person.
|
|
85
|
+
*/
|
|
86
|
+
export interface Me3Intents {
|
|
87
|
+
/** Newsletter subscription */
|
|
88
|
+
subscribe?: Me3IntentSubscribe;
|
|
89
|
+
/** Meeting booking */
|
|
90
|
+
book?: Me3IntentBook;
|
|
91
|
+
}
|
|
38
92
|
export interface Me3Profile {
|
|
39
93
|
/** Protocol version */
|
|
40
94
|
version: string;
|
|
@@ -56,6 +110,17 @@ export interface Me3Profile {
|
|
|
56
110
|
buttons?: Me3Button[];
|
|
57
111
|
/** Custom pages (markdown) */
|
|
58
112
|
pages?: Me3Page[];
|
|
113
|
+
/**
|
|
114
|
+
* Custom footer configuration.
|
|
115
|
+
* - `undefined`: default footer behavior (renderer-defined)
|
|
116
|
+
* - `false`: hide footer (renderer may restrict this to Pro tiers)
|
|
117
|
+
*/
|
|
118
|
+
footer?: Me3Footer | false;
|
|
119
|
+
/**
|
|
120
|
+
* Intents - machine-readable actions that visitors/agents can take.
|
|
121
|
+
* This is the API contract for interacting with the person.
|
|
122
|
+
*/
|
|
123
|
+
intents?: Me3Intents;
|
|
59
124
|
}
|
|
60
125
|
export interface ValidationError {
|
|
61
126
|
field: string;
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,11 @@ const MAX_BUTTONS = 3;
|
|
|
19
19
|
const MAX_BUTTON_TEXT_LENGTH = 30;
|
|
20
20
|
const VALID_BUTTON_STYLES = ["primary", "secondary", "outline"];
|
|
21
21
|
const URL_REGEX = /^https?:\/\/.+/i;
|
|
22
|
+
const MAX_FOOTER_TEXT_LENGTH = 200;
|
|
23
|
+
const MAX_FOOTER_LINK_TEXT_LENGTH = 60;
|
|
24
|
+
const MAX_INTENT_TITLE_LENGTH = 100;
|
|
25
|
+
const MAX_INTENT_DESCRIPTION_LENGTH = 300;
|
|
26
|
+
const VALID_FREQUENCIES = ["daily", "weekly", "monthly", "irregular"];
|
|
22
27
|
/**
|
|
23
28
|
* Validate a me3 profile object
|
|
24
29
|
*/
|
|
@@ -167,6 +172,70 @@ function validateProfile(data) {
|
|
|
167
172
|
});
|
|
168
173
|
}
|
|
169
174
|
}
|
|
175
|
+
// Footer (optional)
|
|
176
|
+
if (profile.footer !== undefined) {
|
|
177
|
+
if (profile.footer === false) {
|
|
178
|
+
// ok (renderer may enforce tier restrictions)
|
|
179
|
+
}
|
|
180
|
+
else if (typeof profile.footer !== "object" || profile.footer === null) {
|
|
181
|
+
errors.push({
|
|
182
|
+
field: "footer",
|
|
183
|
+
message: "Footer must be an object or false",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const footer = profile.footer;
|
|
188
|
+
if (footer.text !== undefined) {
|
|
189
|
+
if (typeof footer.text !== "string") {
|
|
190
|
+
errors.push({
|
|
191
|
+
field: "footer.text",
|
|
192
|
+
message: "Footer text must be a string",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else if (footer.text.length > MAX_FOOTER_TEXT_LENGTH) {
|
|
196
|
+
errors.push({
|
|
197
|
+
field: "footer.text",
|
|
198
|
+
message: `Footer text must be ${MAX_FOOTER_TEXT_LENGTH} characters or less`,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (footer.link !== undefined) {
|
|
203
|
+
if (typeof footer.link !== "object" || footer.link === null) {
|
|
204
|
+
errors.push({
|
|
205
|
+
field: "footer.link",
|
|
206
|
+
message: "Footer link must be an object",
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
const link = footer.link;
|
|
211
|
+
if (!link.text || typeof link.text !== "string") {
|
|
212
|
+
errors.push({
|
|
213
|
+
field: "footer.link.text",
|
|
214
|
+
message: "Footer link text is required",
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
else if (link.text.length > MAX_FOOTER_LINK_TEXT_LENGTH) {
|
|
218
|
+
errors.push({
|
|
219
|
+
field: "footer.link.text",
|
|
220
|
+
message: `Footer link text must be ${MAX_FOOTER_LINK_TEXT_LENGTH} characters or less`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (!link.url || typeof link.url !== "string") {
|
|
224
|
+
errors.push({
|
|
225
|
+
field: "footer.link.url",
|
|
226
|
+
message: "Footer link URL is required",
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else if (!URL_REGEX.test(link.url)) {
|
|
230
|
+
errors.push({
|
|
231
|
+
field: "footer.link.url",
|
|
232
|
+
message: "Footer link URL must be a valid URL starting with http:// or https://",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
170
239
|
// Pages (optional)
|
|
171
240
|
if (profile.pages !== undefined) {
|
|
172
241
|
if (!Array.isArray(profile.pages)) {
|
|
@@ -208,6 +277,141 @@ function validateProfile(data) {
|
|
|
208
277
|
});
|
|
209
278
|
}
|
|
210
279
|
}
|
|
280
|
+
// Intents (optional)
|
|
281
|
+
if (profile.intents !== undefined) {
|
|
282
|
+
if (typeof profile.intents !== "object" || profile.intents === null) {
|
|
283
|
+
errors.push({ field: "intents", message: "Intents must be an object" });
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
const intents = profile.intents;
|
|
287
|
+
// Validate subscribe intent
|
|
288
|
+
if (intents.subscribe !== undefined) {
|
|
289
|
+
if (typeof intents.subscribe !== "object" ||
|
|
290
|
+
intents.subscribe === null) {
|
|
291
|
+
errors.push({
|
|
292
|
+
field: "intents.subscribe",
|
|
293
|
+
message: "Subscribe intent must be an object",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const subscribe = intents.subscribe;
|
|
298
|
+
if (typeof subscribe.enabled !== "boolean") {
|
|
299
|
+
errors.push({
|
|
300
|
+
field: "intents.subscribe.enabled",
|
|
301
|
+
message: "Subscribe enabled must be a boolean",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (subscribe.title !== undefined) {
|
|
305
|
+
if (typeof subscribe.title !== "string") {
|
|
306
|
+
errors.push({
|
|
307
|
+
field: "intents.subscribe.title",
|
|
308
|
+
message: "Subscribe title must be a string",
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
else if (subscribe.title.length > MAX_INTENT_TITLE_LENGTH) {
|
|
312
|
+
errors.push({
|
|
313
|
+
field: "intents.subscribe.title",
|
|
314
|
+
message: `Subscribe title must be ${MAX_INTENT_TITLE_LENGTH} characters or less`,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (subscribe.description !== undefined) {
|
|
319
|
+
if (typeof subscribe.description !== "string") {
|
|
320
|
+
errors.push({
|
|
321
|
+
field: "intents.subscribe.description",
|
|
322
|
+
message: "Subscribe description must be a string",
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else if (subscribe.description.length > MAX_INTENT_DESCRIPTION_LENGTH) {
|
|
326
|
+
errors.push({
|
|
327
|
+
field: "intents.subscribe.description",
|
|
328
|
+
message: `Subscribe description must be ${MAX_INTENT_DESCRIPTION_LENGTH} characters or less`,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (subscribe.frequency !== undefined &&
|
|
333
|
+
!VALID_FREQUENCIES.includes(subscribe.frequency)) {
|
|
334
|
+
errors.push({
|
|
335
|
+
field: "intents.subscribe.frequency",
|
|
336
|
+
message: `Subscribe frequency must be one of: ${VALID_FREQUENCIES.join(", ")}`,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Validate book intent
|
|
342
|
+
if (intents.book !== undefined) {
|
|
343
|
+
if (typeof intents.book !== "object" || intents.book === null) {
|
|
344
|
+
errors.push({
|
|
345
|
+
field: "intents.book",
|
|
346
|
+
message: "Book intent must be an object",
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const book = intents.book;
|
|
351
|
+
if (typeof book.enabled !== "boolean") {
|
|
352
|
+
errors.push({
|
|
353
|
+
field: "intents.book.enabled",
|
|
354
|
+
message: "Book enabled must be a boolean",
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (book.title !== undefined) {
|
|
358
|
+
if (typeof book.title !== "string") {
|
|
359
|
+
errors.push({
|
|
360
|
+
field: "intents.book.title",
|
|
361
|
+
message: "Book title must be a string",
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else if (book.title.length > MAX_INTENT_TITLE_LENGTH) {
|
|
365
|
+
errors.push({
|
|
366
|
+
field: "intents.book.title",
|
|
367
|
+
message: `Book title must be ${MAX_INTENT_TITLE_LENGTH} characters or less`,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (book.description !== undefined) {
|
|
372
|
+
if (typeof book.description !== "string") {
|
|
373
|
+
errors.push({
|
|
374
|
+
field: "intents.book.description",
|
|
375
|
+
message: "Book description must be a string",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
else if (book.description.length > MAX_INTENT_DESCRIPTION_LENGTH) {
|
|
379
|
+
errors.push({
|
|
380
|
+
field: "intents.book.description",
|
|
381
|
+
message: `Book description must be ${MAX_INTENT_DESCRIPTION_LENGTH} characters or less`,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (book.duration !== undefined &&
|
|
386
|
+
typeof book.duration !== "number") {
|
|
387
|
+
errors.push({
|
|
388
|
+
field: "intents.book.duration",
|
|
389
|
+
message: "Book duration must be a number (minutes)",
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
if (book.provider !== undefined &&
|
|
393
|
+
typeof book.provider !== "string") {
|
|
394
|
+
errors.push({
|
|
395
|
+
field: "intents.book.provider",
|
|
396
|
+
message: "Book provider must be a string",
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
if (!book.url || typeof book.url !== "string") {
|
|
400
|
+
errors.push({
|
|
401
|
+
field: "intents.book.url",
|
|
402
|
+
message: "Book URL is required",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
else if (!URL_REGEX.test(book.url)) {
|
|
406
|
+
errors.push({
|
|
407
|
+
field: "intents.book.url",
|
|
408
|
+
message: "Book URL must be a valid URL starting with http:// or https://",
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
211
415
|
if (errors.length > 0) {
|
|
212
416
|
return { valid: false, errors };
|
|
213
417
|
}
|
package/examples/full.json
CHANGED
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1",
|
|
3
|
-
"name": "
|
|
4
|
-
"handle": "
|
|
5
|
-
"location": "
|
|
6
|
-
"bio": "
|
|
7
|
-
"avatar": "https://api.dicebear.com/7.x/avataaars/svg?seed=
|
|
3
|
+
"name": "Example Name",
|
|
4
|
+
"handle": "example",
|
|
5
|
+
"location": "City, Country",
|
|
6
|
+
"bio": "Short example bio describing what this person does.",
|
|
7
|
+
"avatar": "https://api.dicebear.com/7.x/avataaars/svg?seed=example",
|
|
8
8
|
"banner": "https://images.unsplash.com/photo-1519681393784-d120267933ba?w=1200&h=400&fit=crop",
|
|
9
9
|
"links": {
|
|
10
|
-
"website": "https://
|
|
11
|
-
"github": "
|
|
12
|
-
"soulink": "
|
|
10
|
+
"website": "https://example.com",
|
|
11
|
+
"github": "example",
|
|
12
|
+
"soulink": "example"
|
|
13
13
|
},
|
|
14
14
|
"buttons": [
|
|
15
15
|
{
|
|
16
|
-
"text": "
|
|
17
|
-
"url": "https://cal.com/
|
|
16
|
+
"text": "Primary Call To Action",
|
|
17
|
+
"url": "https://cal.com/example",
|
|
18
18
|
"style": "primary",
|
|
19
19
|
"icon": "📅"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
"text": "
|
|
23
|
-
"url": "https://buymeacoffee.com/
|
|
22
|
+
"text": "Secondary Action",
|
|
23
|
+
"url": "https://buymeacoffee.com/example",
|
|
24
24
|
"style": "secondary",
|
|
25
25
|
"icon": "☕"
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
28
|
"pages": [
|
|
29
29
|
{ "slug": "about", "title": "About", "file": "about.md", "visible": true }
|
|
30
|
-
]
|
|
30
|
+
],
|
|
31
|
+
"intents": {
|
|
32
|
+
"subscribe": {
|
|
33
|
+
"enabled": true,
|
|
34
|
+
"title": "Weekly Insights",
|
|
35
|
+
"description": "Curated thoughts on design, tech, and creativity every Sunday",
|
|
36
|
+
"frequency": "weekly"
|
|
37
|
+
},
|
|
38
|
+
"book": {
|
|
39
|
+
"enabled": true,
|
|
40
|
+
"title": "30-min Consultation",
|
|
41
|
+
"description": "Let's discuss your project",
|
|
42
|
+
"duration": 30,
|
|
43
|
+
"provider": "cal.com",
|
|
44
|
+
"url": "https://cal.com/example"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
}
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -33,6 +33,120 @@
|
|
|
33
33
|
],
|
|
34
34
|
"type": "object"
|
|
35
35
|
},
|
|
36
|
+
"Me3Footer": {
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"properties": {
|
|
39
|
+
"link": {
|
|
40
|
+
"$ref": "#/definitions/Me3FooterLink",
|
|
41
|
+
"description": "Optional custom footer link"
|
|
42
|
+
},
|
|
43
|
+
"text": {
|
|
44
|
+
"description": "Custom footer text (e.g. \"Built by Jane\")",
|
|
45
|
+
"type": "string"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"type": "object"
|
|
49
|
+
},
|
|
50
|
+
"Me3FooterLink": {
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"properties": {
|
|
53
|
+
"text": {
|
|
54
|
+
"description": "Link text",
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
57
|
+
"url": {
|
|
58
|
+
"description": "URL to open when clicked",
|
|
59
|
+
"type": "string"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": [
|
|
63
|
+
"text",
|
|
64
|
+
"url"
|
|
65
|
+
],
|
|
66
|
+
"type": "object"
|
|
67
|
+
},
|
|
68
|
+
"Me3IntentBook": {
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"description": "Booking/scheduling intent. Declares that the person accepts meeting bookings.",
|
|
71
|
+
"properties": {
|
|
72
|
+
"description": {
|
|
73
|
+
"description": "What the meeting is about",
|
|
74
|
+
"type": "string"
|
|
75
|
+
},
|
|
76
|
+
"duration": {
|
|
77
|
+
"description": "Meeting duration in minutes",
|
|
78
|
+
"type": "number"
|
|
79
|
+
},
|
|
80
|
+
"enabled": {
|
|
81
|
+
"description": "Whether booking is enabled",
|
|
82
|
+
"type": "boolean"
|
|
83
|
+
},
|
|
84
|
+
"provider": {
|
|
85
|
+
"description": "Booking provider (e.g., \"cal.com\", \"calendly\")",
|
|
86
|
+
"type": "string"
|
|
87
|
+
},
|
|
88
|
+
"title": {
|
|
89
|
+
"description": "Meeting title (e.g., \"30-min Consultation\")",
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"url": {
|
|
93
|
+
"description": "Direct booking URL",
|
|
94
|
+
"type": "string"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"required": [
|
|
98
|
+
"enabled",
|
|
99
|
+
"url"
|
|
100
|
+
],
|
|
101
|
+
"type": "object"
|
|
102
|
+
},
|
|
103
|
+
"Me3IntentSubscribe": {
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"description": "Newsletter subscription intent. When enabled, the site accepts email subscriptions via POST /api/subscribe",
|
|
106
|
+
"properties": {
|
|
107
|
+
"description": {
|
|
108
|
+
"description": "What subscribers will receive - for agents to explain the value",
|
|
109
|
+
"type": "string"
|
|
110
|
+
},
|
|
111
|
+
"enabled": {
|
|
112
|
+
"description": "Whether newsletter signups are enabled",
|
|
113
|
+
"type": "boolean"
|
|
114
|
+
},
|
|
115
|
+
"frequency": {
|
|
116
|
+
"description": "How often subscribers will hear from you",
|
|
117
|
+
"enum": [
|
|
118
|
+
"daily",
|
|
119
|
+
"weekly",
|
|
120
|
+
"monthly",
|
|
121
|
+
"irregular"
|
|
122
|
+
],
|
|
123
|
+
"type": "string"
|
|
124
|
+
},
|
|
125
|
+
"title": {
|
|
126
|
+
"description": "Newsletter title (e.g., \"AI Weekly\") - for agents to present context",
|
|
127
|
+
"type": "string"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"required": [
|
|
131
|
+
"enabled"
|
|
132
|
+
],
|
|
133
|
+
"type": "object"
|
|
134
|
+
},
|
|
135
|
+
"Me3Intents": {
|
|
136
|
+
"additionalProperties": false,
|
|
137
|
+
"description": "Intents object - declares what actions visitors/agents can take. This is the machine-readable API contract for interacting with a person.",
|
|
138
|
+
"properties": {
|
|
139
|
+
"book": {
|
|
140
|
+
"$ref": "#/definitions/Me3IntentBook",
|
|
141
|
+
"description": "Meeting booking"
|
|
142
|
+
},
|
|
143
|
+
"subscribe": {
|
|
144
|
+
"$ref": "#/definitions/Me3IntentSubscribe",
|
|
145
|
+
"description": "Newsletter subscription"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"type": "object"
|
|
149
|
+
},
|
|
36
150
|
"Me3Links": {
|
|
37
151
|
"additionalProperties": {
|
|
38
152
|
"anyOf": [
|
|
@@ -123,10 +237,26 @@
|
|
|
123
237
|
},
|
|
124
238
|
"type": "array"
|
|
125
239
|
},
|
|
240
|
+
"footer": {
|
|
241
|
+
"anyOf": [
|
|
242
|
+
{
|
|
243
|
+
"$ref": "#/definitions/Me3Footer"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"const": false,
|
|
247
|
+
"type": "boolean"
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"description": "Custom footer configuration.\n- `undefined`: default footer behavior (renderer-defined)\n- `false`: hide footer (renderer may restrict this to Pro tiers)"
|
|
251
|
+
},
|
|
126
252
|
"handle": {
|
|
127
253
|
"description": "Username/handle",
|
|
128
254
|
"type": "string"
|
|
129
255
|
},
|
|
256
|
+
"intents": {
|
|
257
|
+
"$ref": "#/definitions/Me3Intents",
|
|
258
|
+
"description": "Intents - machine-readable actions that visitors/agents can take. This is the API contract for interacting with the person."
|
|
259
|
+
},
|
|
130
260
|
"links": {
|
|
131
261
|
"$ref": "#/definitions/Me3Links",
|
|
132
262
|
"description": "Social and external links"
|