Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 112 additions & 1 deletion openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
{ "name": "Company", "description": "Organization contact info and knowledge base" },
{ "name": "Notifications", "description": "Event subscriptions and delivery log" },
{ "name": "Contracts", "description": "Contract template import and e-signature" },
{ "name": "Appointments", "description": "Appointment scheduling for presentation viewers" },
{ "name": "Fonts", "description": "Available Google Fonts for branding" }
],
"components": {
Expand Down Expand Up @@ -2935,7 +2936,7 @@
"required": ["name", "eventType", "channel", "channelConfig"],
"properties": {
"name": { "type": "string", "example": "Slack — new presentation" },
"eventType": { "type": "string", "example": "presentation.created" },
"eventType": { "type": "string", "example": "presentation.created", "description": "Event type to subscribe to (e.g. `presentation.opened`, `slide.viewed`, `cta.clicked`, `contract.signed`, `question.asked`, `appointment.booked`)" },
"channel": { "type": "string", "enum": ["webhook", "email", "console"] },
"channelConfig": { "type": "object", "description": "Channel-specific config. Webhook: `{ url }`. Email: `{ recipients: [\"a@b.com\"] }`." },
"deckId": { "type": "string", "format": "uuid", "description": "Scope to a specific deck (optional)" },
Expand Down Expand Up @@ -3305,6 +3306,116 @@
}
}
},
"/api/presentations/{presentationId}/appointment": {
"get": {
"tags": ["Appointments"],
"summary": "Get available appointment slots",
"description": "Fetches available appointment time slots and any existing appointment for the presentation viewer. Slots are grouped by date in the prospect's timezone.\n\n**Authentication**: Requires a presentation token in the `x-presentation-token` header (not a session cookie).\n\n**Rate limit**: 30 requests per minute per presentation.",
"parameters": [
{ "in": "path", "name": "presentationId", "required": true, "schema": { "type": "string", "format": "uuid" } },
{ "in": "header", "name": "x-presentation-token", "required": true, "schema": { "type": "string" }, "description": "Presentation authentication token" }
],
"responses": {
"200": {
"description": "Available slots and existing appointment",
"headers": {
"Cache-Control": { "schema": { "type": "string", "example": "no-store" } }
},
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"slotsByDate": {
"type": "object",
"description": "Available time slots grouped by date (YYYY-MM-DD keys)",
"additionalProperties": {
"type": "array",
"items": {
"type": "object",
"properties": {
"start": { "type": "string", "format": "date-time", "example": "2026-03-21T15:00:00.000Z" },
"end": { "type": "string", "format": "date-time", "example": "2026-03-21T16:00:00.000Z" }
},
"required": ["start", "end"]
}
}
},
"appointmentLength": { "type": "integer", "description": "Appointment duration in minutes", "example": 60 },
"timezone": { "type": "string", "description": "IANA timezone for the prospect", "example": "America/Denver" },
"existingAppointment": {
"type": "object",
"nullable": true,
"description": "The prospect's existing appointment, if any",
"properties": {
"startTime": { "type": "string", "format": "date-time" },
"endTime": { "type": "string", "format": "date-time" }
}
},
"prospectEmail": { "type": "string", "format": "email", "nullable": true },
"prospectPhone": { "type": "string", "nullable": true }
}
}
}
}
},
"401": { "description": "Missing or invalid presentation token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"404": { "description": "Presentation not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"503": { "description": "Scheduling unavailable (API key not configured or upstream error)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
}
},
"post": {
"tags": ["Appointments"],
"summary": "Book an appointment",
"description": "Books an appointment for the presentation viewer. Optionally updates the prospect's email and phone number.\n\n**Authentication**: Requires a presentation token in the `x-presentation-token` header (not a session cookie).\n\n**Rate limit**: 10 requests per minute per presentation.\n\nA successful booking emits an `appointment.booked` event, which can trigger webhook or email notifications if a matching subscription exists.",
"parameters": [
{ "in": "path", "name": "presentationId", "required": true, "schema": { "type": "string", "format": "uuid" } },
{ "in": "header", "name": "x-presentation-token", "required": true, "schema": { "type": "string" }, "description": "Presentation authentication token" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["startTime", "endTime", "attendeeTZ"],
"properties": {
"startTime": { "type": "string", "format": "date-time", "description": "Appointment start (ISO 8601, must be in the future)", "example": "2026-03-21T15:00:00.000Z" },
"endTime": { "type": "string", "format": "date-time", "description": "Appointment end (must be after startTime)", "example": "2026-03-21T16:00:00.000Z" },
"attendeeTZ": { "type": "string", "description": "Attendee's IANA timezone", "example": "America/Denver" },
"email": { "type": "string", "format": "email", "description": "Prospect email (optional, synced to prospect record)" },
"phone": { "type": "string", "minLength": 1, "description": "Prospect phone (optional, synced to prospect record)" }
}
}
}
}
},
"responses": {
"200": {
"description": "Appointment booked",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": { "type": "boolean", "example": true },
"startTime": { "type": "string", "format": "date-time" },
"endTime": { "type": "string", "format": "date-time" }
}
}
}
}
},
"400": { "description": "Invalid request body or time validation failure", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"401": { "description": "Missing or invalid presentation token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"404": { "description": "Presentation not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"409": { "description": "Conflict — prospect already has an appointment (`already_booked`) or the slot is no longer available (`slot_unavailable`)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"503": { "description": "Scheduling unavailable (API key not configured or upstream error)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
}
}
},
"/api/address/validate": {
"post": {
"tags": ["Utility"],
Expand Down
1 change: 1 addition & 0 deletions sales-copilot/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Sales CoPilot by Demand IQ lets you build AI-narrated sales presentations with i
- **Product pricing** — configure good/better/best product tiers on slides with dynamic pricing formulas
- **Knowledge base** — upload company documents to improve AI-generated answers with your specific product and service information
- **Contracts & e-signature** — import contract templates, configure signing requirements, and collect typed signatures during the presentation
- **Appointment scheduling** — let viewers book appointments directly from the presentation, with available time slots pulled from your calendar
- **Notifications** — subscribe to presentation events via webhooks or email to track viewer engagement in real time
- **Multi-language support** — present in English, Spanish, French, German, Portuguese, Italian, Chinese, or Japanese

Expand Down
113 changes: 112 additions & 1 deletion sales-copilot/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
{ "name": "Company", "description": "Organization contact info and knowledge base" },
{ "name": "Notifications", "description": "Event subscriptions and delivery log" },
{ "name": "Contracts", "description": "Contract template import and e-signature" },
{ "name": "Appointments", "description": "Appointment scheduling for presentation viewers" },
{ "name": "Fonts", "description": "Available Google Fonts for branding" }
],
"components": {
Expand Down Expand Up @@ -2935,7 +2936,7 @@
"required": ["name", "eventType", "channel", "channelConfig"],
"properties": {
"name": { "type": "string", "example": "Slack — new presentation" },
"eventType": { "type": "string", "example": "presentation.created" },
"eventType": { "type": "string", "example": "presentation.created", "description": "Event type to subscribe to (e.g. `presentation.opened`, `slide.viewed`, `cta.clicked`, `contract.signed`, `question.asked`, `appointment.booked`)" },
"channel": { "type": "string", "enum": ["webhook", "email", "console"] },
"channelConfig": { "type": "object", "description": "Channel-specific config. Webhook: `{ url }`. Email: `{ recipients: [\"a@b.com\"] }`." },
"deckId": { "type": "string", "format": "uuid", "description": "Scope to a specific deck (optional)" },
Expand Down Expand Up @@ -3305,6 +3306,116 @@
}
}
},
"/api/presentations/{presentationId}/appointment": {
"get": {
"tags": ["Appointments"],
"summary": "Get available appointment slots",
"description": "Fetches available appointment time slots and any existing appointment for the presentation viewer. Slots are grouped by date in the prospect's timezone.\n\n**Authentication**: Requires a presentation token in the `x-presentation-token` header (not a session cookie).\n\n**Rate limit**: 30 requests per minute per presentation.",
"parameters": [
{ "in": "path", "name": "presentationId", "required": true, "schema": { "type": "string", "format": "uuid" } },
{ "in": "header", "name": "x-presentation-token", "required": true, "schema": { "type": "string" }, "description": "Presentation authentication token" }
],
"responses": {
"200": {
"description": "Available slots and existing appointment",
"headers": {
"Cache-Control": { "schema": { "type": "string", "example": "no-store" } }
},
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"slotsByDate": {
"type": "object",
"description": "Available time slots grouped by date (YYYY-MM-DD keys)",
"additionalProperties": {
"type": "array",
"items": {
"type": "object",
"properties": {
"start": { "type": "string", "format": "date-time", "example": "2026-03-21T15:00:00.000Z" },
"end": { "type": "string", "format": "date-time", "example": "2026-03-21T16:00:00.000Z" }
},
"required": ["start", "end"]
}
}
},
"appointmentLength": { "type": "integer", "description": "Appointment duration in minutes", "example": 60 },
"timezone": { "type": "string", "description": "IANA timezone for the prospect", "example": "America/Denver" },
"existingAppointment": {
"type": "object",
"nullable": true,
"description": "The prospect's existing appointment, if any",
"properties": {
"startTime": { "type": "string", "format": "date-time" },
"endTime": { "type": "string", "format": "date-time" }
}
},
"prospectEmail": { "type": "string", "format": "email", "nullable": true },
"prospectPhone": { "type": "string", "nullable": true }
}
}
}
}
},
"401": { "description": "Missing or invalid presentation token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"404": { "description": "Presentation not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"503": { "description": "Scheduling unavailable (API key not configured or upstream error)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
}
},
"post": {
"tags": ["Appointments"],
"summary": "Book an appointment",
"description": "Books an appointment for the presentation viewer. Optionally updates the prospect's email and phone number.\n\n**Authentication**: Requires a presentation token in the `x-presentation-token` header (not a session cookie).\n\n**Rate limit**: 10 requests per minute per presentation.\n\nA successful booking emits an `appointment.booked` event, which can trigger webhook or email notifications if a matching subscription exists.",
"parameters": [
{ "in": "path", "name": "presentationId", "required": true, "schema": { "type": "string", "format": "uuid" } },
{ "in": "header", "name": "x-presentation-token", "required": true, "schema": { "type": "string" }, "description": "Presentation authentication token" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["startTime", "endTime", "attendeeTZ"],
"properties": {
"startTime": { "type": "string", "format": "date-time", "description": "Appointment start (ISO 8601, must be in the future)", "example": "2026-03-21T15:00:00.000Z" },
"endTime": { "type": "string", "format": "date-time", "description": "Appointment end (must be after startTime)", "example": "2026-03-21T16:00:00.000Z" },
"attendeeTZ": { "type": "string", "description": "Attendee's IANA timezone", "example": "America/Denver" },
"email": { "type": "string", "format": "email", "description": "Prospect email (optional, synced to prospect record)" },
"phone": { "type": "string", "minLength": 1, "description": "Prospect phone (optional, synced to prospect record)" }
}
}
}
}
},
"responses": {
"200": {
"description": "Appointment booked",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": { "type": "boolean", "example": true },
"startTime": { "type": "string", "format": "date-time" },
"endTime": { "type": "string", "format": "date-time" }
}
}
}
}
},
"400": { "description": "Invalid request body or time validation failure", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"401": { "description": "Missing or invalid presentation token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"404": { "description": "Presentation not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"409": { "description": "Conflict — prospect already has an appointment (`already_booked`) or the slot is no longer available (`slot_unavailable`)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
"503": { "description": "Scheduling unavailable (API key not configured or upstream error)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
}
}
},
"/api/address/validate": {
"post": {
"tags": ["Utility"],
Expand Down