diff --git a/apps/docs/api-reference/contacts/create-contact-book.mdx b/apps/docs/api-reference/contacts/create-contact-book.mdx new file mode 100644 index 00000000..0895fa6e --- /dev/null +++ b/apps/docs/api-reference/contacts/create-contact-book.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/contactBooks +--- diff --git a/apps/docs/api-reference/contacts/delete-contact-book.mdx b/apps/docs/api-reference/contacts/delete-contact-book.mdx new file mode 100644 index 00000000..3afb1c32 --- /dev/null +++ b/apps/docs/api-reference/contacts/delete-contact-book.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v1/contactBooks/{contactBookId} +--- diff --git a/apps/docs/api-reference/contacts/get-contact-book.mdx b/apps/docs/api-reference/contacts/get-contact-book.mdx new file mode 100644 index 00000000..3dab8e68 --- /dev/null +++ b/apps/docs/api-reference/contacts/get-contact-book.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/contactBooks/{contactBookId} +--- diff --git a/apps/docs/api-reference/contacts/list-contact-books.mdx b/apps/docs/api-reference/contacts/list-contact-books.mdx new file mode 100644 index 00000000..27886117 --- /dev/null +++ b/apps/docs/api-reference/contacts/list-contact-books.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/contactBooks +--- diff --git a/apps/docs/api-reference/contacts/update-contact-book.mdx b/apps/docs/api-reference/contacts/update-contact-book.mdx new file mode 100644 index 00000000..7670494d --- /dev/null +++ b/apps/docs/api-reference/contacts/update-contact-book.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /v1/contactBooks/{contactBookId} +--- diff --git a/apps/docs/api-reference/openapi.json b/apps/docs/api-reference/openapi.json index af5ffbd1..65f74a46 100644 --- a/apps/docs/api-reference/openapi.json +++ b/apps/docs/api-reference/openapi.json @@ -1,1805 +1,2048 @@ { - "openapi": "3.0.0", - "info": { "version": "1.0.0", "title": "useSend API" }, - "servers": [{ "url": "https://app.usesend.com/api" }], - "components": { - "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer" } }, - "schemas": {}, - "parameters": {} - }, - "paths": { - "/v1/domains": { - "get": { - "responses": { - "200": { - "description": "Retrieve domains accessible by the API key", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "number", - "description": "The ID of the domain", - "example": 1 - }, - "name": { - "type": "string", - "description": "The name of the domain", - "example": "example.com" - }, - "teamId": { - "type": "number", - "description": "The ID of the team", - "example": 1 - }, - "status": { - "type": "string", - "enum": [ - "NOT_STARTED", - "PENDING", - "SUCCESS", - "FAILED", - "TEMPORARY_FAILURE" - ] - }, - "region": { "type": "string", "default": "us-east-1" }, - "clickTracking": { "type": "boolean", "default": false }, - "openTracking": { "type": "boolean", "default": false }, - "publicKey": { "type": "string" }, - "dkimStatus": { "type": "string", "nullable": true }, - "spfDetails": { "type": "string", "nullable": true }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" }, - "dmarcAdded": { "type": "boolean", "default": false }, - "isVerifying": { "type": "boolean", "default": false }, - "errorMessage": { "type": "string", "nullable": true }, - "subdomain": { "type": "string", "nullable": true }, - "verificationError": { - "type": "string", - "nullable": true - }, - "lastCheckedTime": { "type": "string", "nullable": true }, - "dnsRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["MX", "TXT"], - "description": "DNS record type", - "example": "TXT" - }, - "name": { - "type": "string", - "description": "DNS record name", - "example": "mail" - }, - "value": { - "type": "string", - "description": "DNS record value", - "example": "v=spf1 include:amazonses.com ~all" - }, - "ttl": { - "type": "string", - "description": "DNS record TTL", - "example": "Auto" - }, - "priority": { - "type": "string", - "nullable": true, - "description": "DNS record priority", - "example": "10" - }, - "status": { - "type": "string", - "enum": [ - "NOT_STARTED", - "PENDING", - "SUCCESS", - "FAILED", - "TEMPORARY_FAILURE" - ] - }, - "recommended": { - "type": "boolean", - "description": "Whether the record is recommended" - } - }, - "required": ["type", "name", "value", "ttl", "status"] - } - } - }, - "required": [ - "id", - "name", - "teamId", - "status", - "publicKey", - "createdAt", - "updatedAt", - "dnsRecords" - ] - } - } - } - } - } - } - }, - "post": { - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "region": { "type": "string" } - }, - "required": ["name", "region"] - } - } - } - }, - "responses": { - "200": { - "description": "Create a new domain", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "number", - "description": "The ID of the domain", - "example": 1 - }, - "name": { - "type": "string", - "description": "The name of the domain", - "example": "example.com" - }, - "teamId": { - "type": "number", - "description": "The ID of the team", - "example": 1 - }, - "status": { - "type": "string", - "enum": [ - "NOT_STARTED", - "PENDING", - "SUCCESS", - "FAILED", - "TEMPORARY_FAILURE" - ] - }, - "region": { "type": "string", "default": "us-east-1" }, - "clickTracking": { "type": "boolean", "default": false }, - "openTracking": { "type": "boolean", "default": false }, - "publicKey": { "type": "string" }, - "dkimStatus": { "type": "string", "nullable": true }, - "spfDetails": { "type": "string", "nullable": true }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" }, - "dmarcAdded": { "type": "boolean", "default": false }, - "isVerifying": { "type": "boolean", "default": false }, - "errorMessage": { "type": "string", "nullable": true }, - "subdomain": { "type": "string", "nullable": true }, - "verificationError": { "type": "string", "nullable": true }, - "lastCheckedTime": { "type": "string", "nullable": true }, - "dnsRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["MX", "TXT"], - "description": "DNS record type", - "example": "TXT" - }, - "name": { - "type": "string", - "description": "DNS record name", - "example": "mail" - }, - "value": { - "type": "string", - "description": "DNS record value", - "example": "v=spf1 include:amazonses.com ~all" - }, - "ttl": { - "type": "string", - "description": "DNS record TTL", - "example": "Auto" - }, - "priority": { - "type": "string", - "nullable": true, - "description": "DNS record priority", - "example": "10" - }, - "status": { - "type": "string", - "enum": [ - "NOT_STARTED", - "PENDING", - "SUCCESS", - "FAILED", - "TEMPORARY_FAILURE" - ] - }, - "recommended": { - "type": "boolean", - "description": "Whether the record is recommended" - } - }, - "required": ["type", "name", "value", "ttl", "status"] - } - } - }, - "required": [ - "id", - "name", - "teamId", - "status", - "publicKey", - "createdAt", - "updatedAt", - "dnsRecords" - ] - } - } - } - } - } - } - }, - "/v1/domains/{id}/verify": { - "put": { - "parameters": [ - { - "schema": { "type": "number", "nullable": true, "example": 1 }, - "required": false, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Verify domain", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "message": { "type": "string" } }, - "required": ["message"] - } - } - } - }, - "403": { - "description": "Forbidden - API key doesn't have access to this domain", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "error": { "type": "string" } }, - "required": ["error"] - } - } - } - }, - "404": { - "description": "Domain not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "error": { "type": "string" } }, - "required": ["error"] - } - } - } - } - } - } - }, - "/v1/domains/{id}": { - "get": { - "parameters": [ - { - "schema": { "type": "number", "nullable": true, "example": 1 }, - "required": false, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Retrieve the domain", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "number", - "description": "The ID of the domain", - "example": 1 - }, - "name": { - "type": "string", - "description": "The name of the domain", - "example": "example.com" - }, - "teamId": { - "type": "number", - "description": "The ID of the team", - "example": 1 - }, - "status": { - "type": "string", - "enum": [ - "NOT_STARTED", - "PENDING", - "SUCCESS", - "FAILED", - "TEMPORARY_FAILURE" - ] - }, - "region": { "type": "string", "default": "us-east-1" }, - "clickTracking": { "type": "boolean", "default": false }, - "openTracking": { "type": "boolean", "default": false }, - "publicKey": { "type": "string" }, - "dkimStatus": { "type": "string", "nullable": true }, - "spfDetails": { "type": "string", "nullable": true }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" }, - "dmarcAdded": { "type": "boolean", "default": false }, - "isVerifying": { "type": "boolean", "default": false }, - "errorMessage": { "type": "string", "nullable": true }, - "subdomain": { "type": "string", "nullable": true }, - "verificationError": { "type": "string", "nullable": true }, - "lastCheckedTime": { "type": "string", "nullable": true }, - "dnsRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["MX", "TXT"], - "description": "DNS record type", - "example": "TXT" - }, - "name": { - "type": "string", - "description": "DNS record name", - "example": "mail" - }, - "value": { - "type": "string", - "description": "DNS record value", - "example": "v=spf1 include:amazonses.com ~all" - }, - "ttl": { - "type": "string", - "description": "DNS record TTL", - "example": "Auto" - }, - "priority": { - "type": "string", - "nullable": true, - "description": "DNS record priority", - "example": "10" - }, - "status": { - "type": "string", - "enum": [ - "NOT_STARTED", - "PENDING", - "SUCCESS", - "FAILED", - "TEMPORARY_FAILURE" - ] - }, - "recommended": { - "type": "boolean", - "description": "Whether the record is recommended" - } - }, - "required": ["type", "name", "value", "ttl", "status"] - } - } - }, - "required": [ - "id", - "name", - "teamId", - "status", - "publicKey", - "createdAt", - "updatedAt", - "dnsRecords" - ] - } - } - } - } - } - }, - "delete": { - "parameters": [ - { - "schema": { "type": "number", "nullable": true, "example": 1 }, - "required": false, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Domain deleted successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "number" }, - "success": { "type": "boolean" }, - "message": { "type": "string" } - }, - "required": ["id", "success", "message"] - } - } - } - }, - "403": { - "description": "Forbidden - API key doesn't have access", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "error": { "type": "string" } }, - "required": ["error"] - } - } - } - }, - "404": { - "description": "Domain not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "error": { "type": "string" } }, - "required": ["error"] - } - } - } - } - } - } - }, - "/v1/emails/{emailId}": { - "get": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 3, - "example": "cuiwqdj74rygf74" - }, - "required": true, - "name": "emailId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Retrieve the email", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "teamId": { "type": "number" }, - "to": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "replyTo": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "cc": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "bcc": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "from": { "type": "string" }, - "subject": { "type": "string" }, - "html": { "type": "string", "nullable": true }, - "text": { "type": "string", "nullable": true }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" }, - "emailEvents": { - "type": "array", - "items": { - "type": "object", - "properties": { - "emailId": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "SCHEDULED", - "QUEUED", - "SENT", - "DELIVERY_DELAYED", - "BOUNCED", - "REJECTED", - "RENDERING_FAILURE", - "DELIVERED", - "OPENED", - "CLICKED", - "COMPLAINED", - "FAILED", - "CANCELLED", - "SUPPRESSED" - ] - }, - "createdAt": { "type": "string" }, - "data": { "nullable": true } - }, - "required": ["emailId", "status", "createdAt"] - } - } - }, - "required": [ - "id", - "teamId", - "to", - "from", - "subject", - "html", - "text", - "createdAt", - "updatedAt", - "emailEvents" - ] - } - } - } - } - } - }, - "patch": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 3, - "example": "cuiwqdj74rygf74" - }, - "required": true, - "name": "emailId", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "scheduledAt": { "type": "string", "format": "date-time" } - }, - "required": ["scheduledAt"] - } - } - } - }, - "responses": { - "200": { - "description": "Retrieve the user", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "emailId": { "type": "string" } } - } - } - } - } - } - } - }, - "/v1/emails": { - "get": { - "parameters": [ - { - "schema": { "type": "string", "default": "1", "example": "1" }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { "type": "string", "default": "50", "example": "50" }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "format": "date-time", - "example": "2024-01-01T00:00:00Z" - }, - "required": false, - "name": "startDate", - "in": "query" - }, - { - "schema": { - "type": "string", - "format": "date-time", - "example": "2024-01-31T23:59:59Z" - }, - "required": false, - "name": "endDate", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ], - "example": "123" - }, - "required": false, - "name": "domainId", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Retrieve a list of emails", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "to": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "replyTo": { - "anyOf": [ - { "type": "string" }, - { - "type": "array", - "items": { "type": "string" } - }, - { "nullable": true } - ] - }, - "cc": { - "anyOf": [ - { "type": "string" }, - { - "type": "array", - "items": { "type": "string" } - }, - { "nullable": true } - ] - }, - "bcc": { - "anyOf": [ - { "type": "string" }, - { - "type": "array", - "items": { "type": "string" } - }, - { "nullable": true } - ] - }, - "from": { "type": "string" }, - "subject": { "type": "string" }, - "html": { "type": "string", "nullable": true }, - "text": { "type": "string", "nullable": true }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" }, - "latestStatus": { - "type": "string", - "nullable": true, - "enum": [ - "SCHEDULED", - "QUEUED", - "SENT", - "DELIVERY_DELAYED", - "BOUNCED", - "REJECTED", - "RENDERING_FAILURE", - "DELIVERED", - "OPENED", - "CLICKED", - "COMPLAINED", - "FAILED", - "CANCELLED", - "SUPPRESSED" - ] - }, - "scheduledAt": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "domainId": { "type": "number", "nullable": true } - }, - "required": [ - "id", - "to", - "from", - "subject", - "html", - "text", - "createdAt", - "updatedAt", - "latestStatus", - "scheduledAt", - "domainId" - ] - } - }, - "count": { "type": "number" } - }, - "required": ["data", "count"] - } - } - } - } - } - }, - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 256, - "description": "Pass the optional Idempotency-Key header to make the request safe to retry. The key can be up to 256 characters. The server stores the canonical request body and behaves as follows:\n\n- Same key + same request body → returns the original emailId with 200 OK without re-sending.\n- Same key + different request body → returns 409 Conflict with code: NOT_UNIQUE so you can detect the mismatch.\n- Same key while another request is still being processed → returns 409 Conflict; retry after a short delay or once the first request completes.\n\nEntries expire after 24 hours. Use a unique key per logical send (for example, an order or signup ID)." - }, - "required": false, - "name": "Idempotency-Key", - "in": "header" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "to": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "from": { "type": "string" }, - "subject": { - "type": "string", - "minLength": 1, - "description": "Optional when templateId is provided" - }, - "templateId": { - "type": "string", - "description": "ID of a template from the dashboard" - }, - "variables": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "replyTo": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "cc": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "bcc": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "text": { - "type": "string", - "nullable": true, - "minLength": 1 - }, - "html": { - "type": "string", - "nullable": true, - "minLength": 1 - }, - "headers": { - "type": "object", - "additionalProperties": { - "type": "string", - "minLength": 1 - }, - "description": "Custom headers to included with the emails" - }, - "attachments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "filename": { "type": "string", "minLength": 1 }, - "content": { "type": "string", "minLength": 1 } - }, - "required": ["filename", "content"] - }, - "maxItems": 10 - }, - "scheduledAt": { "type": "string", "format": "date-time" }, - "inReplyToId": { "type": "string", "nullable": true } - }, - "required": ["to", "from"] - } - } - } - }, - "responses": { - "200": { - "description": "Retrieve the user", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "emailId": { "type": "string" } } - } - } - } - } - } - } - }, - "/v1/emails/batch": { - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 256, - "description": "Pass the optional Idempotency-Key header to make the request safe to retry. The key can be up to 256 characters. The server stores the canonical request body and behaves as follows:\n\n- Same key + same request body → returns the original emailId with 200 OK without re-sending.\n- Same key + different request body → returns 409 Conflict with code: NOT_UNIQUE so you can detect the mismatch.\n- Same key while another request is still being processed → returns 409 Conflict; retry after a short delay or once the first request completes.\n\nEntries expire after 24 hours. Use a unique key per logical send (for example, an order or signup ID)." - }, - "required": false, - "name": "Idempotency-Key", - "in": "header" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "to": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "from": { "type": "string" }, - "subject": { - "type": "string", - "minLength": 1, - "description": "Optional when templateId is provided" - }, - "templateId": { - "type": "string", - "description": "ID of a template from the dashboard" - }, - "variables": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "replyTo": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "cc": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "bcc": { - "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - }, - "text": { - "type": "string", - "nullable": true, - "minLength": 1 - }, - "html": { - "type": "string", - "nullable": true, - "minLength": 1 - }, - "headers": { - "type": "object", - "additionalProperties": { - "type": "string", - "minLength": 1 - }, - "description": "Custom headers to included with the emails" - }, - "attachments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "filename": { "type": "string", "minLength": 1 }, - "content": { "type": "string", "minLength": 1 } - }, - "required": ["filename", "content"] - }, - "maxItems": 10 - }, - "scheduledAt": { "type": "string", "format": "date-time" }, - "inReplyToId": { "type": "string", "nullable": true } - }, - "required": ["to", "from"] - }, - "maxItems": 100 - } - } - } - }, - "responses": { - "200": { - "description": "List of successfully created email IDs", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object", - "properties": { "emailId": { "type": "string" } }, - "required": ["emailId"] - } - } - }, - "required": ["data"] - } - } - } - } - } - } - }, - "/v1/emails/{emailId}/cancel": { - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 3, - "example": "cuiwqdj74rygf74" - }, - "required": true, - "name": "emailId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Retrieve the user", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "emailId": { "type": "string" } } - } - } - } - } - } - } - }, - "/v1/contactBooks/{contactBookId}/contacts": { - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 3, - "example": "cuiwqdj74rygf74" - }, - "required": true, - "name": "contactBookId", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { "type": "string" }, - "firstName": { "type": "string" }, - "lastName": { "type": "string" }, - "properties": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "subscribed": { "type": "boolean" } - }, - "required": ["email"] - } - } - } - }, - "responses": { - "200": { - "description": "Retrieve the user", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "contactId": { "type": "string" } } - } - } - } - } - } - }, - "get": { - "parameters": [ - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactBookId", - "in": "path" - }, - { - "schema": { "type": "string" }, - "required": false, - "name": "emails", - "in": "query" - }, - { - "schema": { "type": "number" }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { "type": "number" }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { "type": "string" }, - "required": false, - "name": "ids", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Retrieve multiple contacts", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "firstName": { "type": "string", "nullable": true }, - "lastName": { "type": "string", "nullable": true }, - "email": { "type": "string" }, - "subscribed": { "type": "boolean", "default": true }, - "properties": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "contactBookId": { "type": "string" }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" } - }, - "required": [ - "id", - "email", - "properties", - "contactBookId", - "createdAt", - "updatedAt" - ] - } - } - } - } - } - } - } - }, - "/v1/contactBooks/{contactBookId}/contacts/{contactId}": { - "patch": { - "parameters": [ - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactBookId", - "in": "path" - }, - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactId", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "firstName": { "type": "string" }, - "lastName": { "type": "string" }, - "properties": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "subscribed": { "type": "boolean" } - } - } - } - } - }, - "responses": { - "200": { - "description": "Retrieve the user", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "contactId": { "type": "string" } } - } - } - } - } - } - }, - "get": { - "parameters": [ - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactBookId", - "in": "path" - }, - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Retrieve the contact", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "firstName": { "type": "string", "nullable": true }, - "lastName": { "type": "string", "nullable": true }, - "email": { "type": "string" }, - "subscribed": { "type": "boolean", "default": true }, - "properties": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "contactBookId": { "type": "string" }, - "createdAt": { "type": "string" }, - "updatedAt": { "type": "string" } - }, - "required": [ - "id", - "email", - "properties", - "contactBookId", - "createdAt", - "updatedAt" - ] - } - } - } - } - } - }, - "put": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 3, - "example": "cuiwqdj74rygf74" - }, - "required": true, - "name": "contactBookId", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { "type": "string" }, - "firstName": { "type": "string" }, - "lastName": { "type": "string" }, - "properties": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "subscribed": { "type": "boolean" } - }, - "required": ["email"] - } - } - } - }, - "responses": { - "200": { - "description": "Contact upserted successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "contactId": { "type": "string" } }, - "required": ["contactId"] - } - } - } - } - } - }, - "delete": { - "parameters": [ - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactBookId", - "in": "path" - }, - { - "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, - "required": true, - "name": "contactId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Contact deleted successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "success": { "type": "boolean" } }, - "required": ["success"] - } - } - } - } - } - } - }, - "/v1/campaigns": { - "get": { - "parameters": [ - { - "schema": { "type": "string", "example": "1" }, - "required": false, - "name": "page", - "in": "query", - "description": "Page number for pagination (default: 1)" - }, - { - "schema": { - "type": "string", - "enum": [ - "DRAFT", - "SCHEDULED", - "SENDING", - "PAUSED", - "SENT", - "CANCELLED" - ], - "example": "DRAFT" - }, - "required": false, - "name": "status", - "in": "query", - "description": "Filter campaigns by status" - }, - { - "schema": { "type": "string", "example": "newsletter" }, - "required": false, - "name": "search", - "in": "query", - "description": "Search campaigns by name or subject" - } - ], - "responses": { - "200": { - "description": "Get list of campaigns", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "campaigns": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "name": { "type": "string" }, - "from": { "type": "string" }, - "subject": { "type": "string" }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "enum": [ - "DRAFT", - "SCHEDULED", - "SENDING", - "PAUSED", - "SENT", - "CANCELLED" - ] - }, - "scheduledAt": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "total": { "type": "integer" }, - "sent": { "type": "integer" }, - "delivered": { "type": "integer" }, - "unsubscribed": { "type": "integer" } - }, - "required": [ - "id", - "name", - "from", - "subject", - "createdAt", - "updatedAt", - "status", - "scheduledAt", - "total", - "sent", - "delivered", - "unsubscribed" - ] - } - }, - "totalPage": { "type": "integer" } - }, - "required": ["campaigns", "totalPage"] - } - } - } - } - } - }, - "post": { - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { "type": "string", "minLength": 1 }, - "from": { "type": "string", "minLength": 1 }, - "subject": { "type": "string", "minLength": 1 }, - "previewText": { "type": "string" }, - "contactBookId": { "type": "string", "minLength": 1 }, - "content": { "type": "string", "minLength": 1 }, - "html": { "type": "string", "minLength": 1 }, - "replyTo": { - "anyOf": [ - { "type": "string", "minLength": 1 }, - { - "type": "array", - "items": { "type": "string", "minLength": 1 } - } - ] - }, - "cc": { - "anyOf": [ - { "type": "string", "minLength": 1 }, - { - "type": "array", - "items": { "type": "string", "minLength": 1 } - } - ] - }, - "bcc": { - "anyOf": [ - { "type": "string", "minLength": 1 }, - { - "type": "array", - "items": { "type": "string", "minLength": 1 } - } - ] - }, - "sendNow": { "type": "boolean" }, - "scheduledAt": { - "type": "string", - "description": "Timestamp in ISO 8601 format or natural language (e.g., 'tomorrow 9am', 'next monday 10:30')" - }, - "batchSize": { - "type": "integer", - "minimum": 1, - "maximum": 100000 - } - }, - "required": ["name", "from", "subject", "contactBookId"] - } - } - } - }, - "responses": { - "200": { - "description": "Create a campaign", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "name": { "type": "string" }, - "from": { "type": "string" }, - "subject": { "type": "string" }, - "previewText": { "type": "string", "nullable": true }, - "contactBookId": { "type": "string", "nullable": true }, - "html": { "type": "string", "nullable": true }, - "content": { "type": "string", "nullable": true }, - "status": { "type": "string" }, - "scheduledAt": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "batchSize": { "type": "integer" }, - "batchWindowMinutes": { "type": "integer" }, - "total": { "type": "integer" }, - "sent": { "type": "integer" }, - "delivered": { "type": "integer" }, - "opened": { "type": "integer" }, - "clicked": { "type": "integer" }, - "unsubscribed": { "type": "integer" }, - "bounced": { "type": "integer" }, - "hardBounced": { "type": "integer" }, - "complained": { "type": "integer" }, - "replyTo": { - "type": "array", - "items": { "type": "string" } - }, - "cc": { "type": "array", "items": { "type": "string" } }, - "bcc": { "type": "array", "items": { "type": "string" } }, - "createdAt": { "type": "string", "format": "date-time" }, - "updatedAt": { "type": "string", "format": "date-time" } - }, - "required": [ - "id", - "name", - "from", - "subject", - "previewText", - "contactBookId", - "html", - "content", - "status", - "scheduledAt", - "batchSize", - "batchWindowMinutes", - "total", - "sent", - "delivered", - "opened", - "clicked", - "unsubscribed", - "bounced", - "hardBounced", - "complained", - "replyTo", - "cc", - "bcc", - "createdAt", - "updatedAt" - ] - } - } - } - } - } - } - }, - "/v1/campaigns/{campaignId}": { - "get": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "example": "cmp_123" - }, - "required": true, - "name": "campaignId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Get campaign details", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "name": { "type": "string" }, - "from": { "type": "string" }, - "subject": { "type": "string" }, - "previewText": { "type": "string", "nullable": true }, - "contactBookId": { "type": "string", "nullable": true }, - "html": { "type": "string", "nullable": true }, - "content": { "type": "string", "nullable": true }, - "status": { "type": "string" }, - "scheduledAt": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "batchSize": { "type": "integer" }, - "batchWindowMinutes": { "type": "integer" }, - "total": { "type": "integer" }, - "sent": { "type": "integer" }, - "delivered": { "type": "integer" }, - "opened": { "type": "integer" }, - "clicked": { "type": "integer" }, - "unsubscribed": { "type": "integer" }, - "bounced": { "type": "integer" }, - "hardBounced": { "type": "integer" }, - "complained": { "type": "integer" }, - "replyTo": { - "type": "array", - "items": { "type": "string" } - }, - "cc": { "type": "array", "items": { "type": "string" } }, - "bcc": { "type": "array", "items": { "type": "string" } }, - "createdAt": { "type": "string", "format": "date-time" }, - "updatedAt": { "type": "string", "format": "date-time" } - }, - "required": [ - "id", - "name", - "from", - "subject", - "previewText", - "contactBookId", - "html", - "content", - "status", - "scheduledAt", - "batchSize", - "batchWindowMinutes", - "total", - "sent", - "delivered", - "opened", - "clicked", - "unsubscribed", - "bounced", - "hardBounced", - "complained", - "replyTo", - "cc", - "bcc", - "createdAt", - "updatedAt" - ] - } - } - } - } - } - } - }, - "/v1/campaigns/{campaignId}/schedule": { - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "example": "cmp_123" - }, - "required": true, - "name": "campaignId", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "scheduledAt": { - "type": "string", - "description": "Timestamp in ISO 8601 format or natural language (e.g., 'tomorrow 9am', 'next monday 10:30')" - }, - "batchSize": { - "type": "integer", - "minimum": 1, - "maximum": 100000 - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Schedule a campaign", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "success": { "type": "boolean" } }, - "required": ["success"] - } - } - } - } - } - } - }, - "/v1/campaigns/{campaignId}/pause": { - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "example": "cmp_123" - }, - "required": true, - "name": "campaignId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Pause a campaign", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "success": { "type": "boolean" } }, - "required": ["success"] - } - } - } - } - } - } - }, - "/v1/campaigns/{campaignId}/resume": { - "post": { - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "example": "cmp_123" - }, - "required": true, - "name": "campaignId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Resume a campaign", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "success": { "type": "boolean" } }, - "required": ["success"] - } - } - } - } - } - } - } - } + "openapi": "3.0.0", + "info": { "version": "1.0.0", "title": "useSend API" }, + "servers": [{ "url": "https://app.usesend.com/api" }], + "components": { + "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer" } }, + "schemas": {}, + "parameters": {} + }, + "paths": { + "/v1/domains": { + "get": { + "responses": { + "200": { + "description": "Retrieve domains accessible by the API key", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "The ID of the domain", + "example": 1 + }, + "name": { + "type": "string", + "description": "The name of the domain", + "example": "example.com" + }, + "teamId": { + "type": "number", + "description": "The ID of the team", + "example": 1 + }, + "status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "region": { "type": "string", "default": "us-east-1" }, + "clickTracking": { "type": "boolean", "default": false }, + "openTracking": { "type": "boolean", "default": false }, + "publicKey": { "type": "string" }, + "dkimStatus": { "type": "string", "nullable": true }, + "spfDetails": { "type": "string", "nullable": true }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "dmarcAdded": { "type": "boolean", "default": false }, + "isVerifying": { "type": "boolean", "default": false }, + "errorMessage": { "type": "string", "nullable": true }, + "subdomain": { "type": "string", "nullable": true }, + "verificationError": { + "type": "string", + "nullable": true + }, + "lastCheckedTime": { "type": "string", "nullable": true }, + "dnsRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["MX", "TXT"], + "description": "DNS record type", + "example": "TXT" + }, + "name": { + "type": "string", + "description": "DNS record name", + "example": "mail" + }, + "value": { + "type": "string", + "description": "DNS record value", + "example": "v=spf1 include:amazonses.com ~all" + }, + "ttl": { + "type": "string", + "description": "DNS record TTL", + "example": "Auto" + }, + "priority": { + "type": "string", + "nullable": true, + "description": "DNS record priority", + "example": "10" + }, + "status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "recommended": { + "type": "boolean", + "description": "Whether the record is recommended" + } + }, + "required": ["type", "name", "value", "ttl", "status"] + } + } + }, + "required": [ + "id", + "name", + "teamId", + "status", + "publicKey", + "createdAt", + "updatedAt", + "dnsRecords" + ] + } + } + } + } + } + } + }, + "post": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "region": { "type": "string" } + }, + "required": ["name", "region"] + } + } + } + }, + "responses": { + "200": { + "description": "Create a new domain", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "The ID of the domain", + "example": 1 + }, + "name": { + "type": "string", + "description": "The name of the domain", + "example": "example.com" + }, + "teamId": { + "type": "number", + "description": "The ID of the team", + "example": 1 + }, + "status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "region": { "type": "string", "default": "us-east-1" }, + "clickTracking": { "type": "boolean", "default": false }, + "openTracking": { "type": "boolean", "default": false }, + "publicKey": { "type": "string" }, + "dkimStatus": { "type": "string", "nullable": true }, + "spfDetails": { "type": "string", "nullable": true }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "dmarcAdded": { "type": "boolean", "default": false }, + "isVerifying": { "type": "boolean", "default": false }, + "errorMessage": { "type": "string", "nullable": true }, + "subdomain": { "type": "string", "nullable": true }, + "verificationError": { "type": "string", "nullable": true }, + "lastCheckedTime": { "type": "string", "nullable": true }, + "dnsRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["MX", "TXT"], + "description": "DNS record type", + "example": "TXT" + }, + "name": { + "type": "string", + "description": "DNS record name", + "example": "mail" + }, + "value": { + "type": "string", + "description": "DNS record value", + "example": "v=spf1 include:amazonses.com ~all" + }, + "ttl": { + "type": "string", + "description": "DNS record TTL", + "example": "Auto" + }, + "priority": { + "type": "string", + "nullable": true, + "description": "DNS record priority", + "example": "10" + }, + "status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "recommended": { + "type": "boolean", + "description": "Whether the record is recommended" + } + }, + "required": ["type", "name", "value", "ttl", "status"] + } + } + }, + "required": [ + "id", + "name", + "teamId", + "status", + "publicKey", + "createdAt", + "updatedAt", + "dnsRecords" + ] + } + } + } + } + } + } + }, + "/v1/domains/{id}/verify": { + "put": { + "parameters": [ + { + "schema": { "type": "number", "nullable": true, "example": 1 }, + "required": false, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Verify domain", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } }, + "required": ["message"] + } + } + } + }, + "403": { + "description": "Forbidden - API key doesn't have access to this domain", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + }, + "404": { + "description": "Domain not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + } + } + } + }, + "/v1/domains/{id}": { + "get": { + "parameters": [ + { + "schema": { "type": "number", "nullable": true, "example": 1 }, + "required": false, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Retrieve the domain", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "The ID of the domain", + "example": 1 + }, + "name": { + "type": "string", + "description": "The name of the domain", + "example": "example.com" + }, + "teamId": { + "type": "number", + "description": "The ID of the team", + "example": 1 + }, + "status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "region": { "type": "string", "default": "us-east-1" }, + "clickTracking": { "type": "boolean", "default": false }, + "openTracking": { "type": "boolean", "default": false }, + "publicKey": { "type": "string" }, + "dkimStatus": { "type": "string", "nullable": true }, + "spfDetails": { "type": "string", "nullable": true }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "dmarcAdded": { "type": "boolean", "default": false }, + "isVerifying": { "type": "boolean", "default": false }, + "errorMessage": { "type": "string", "nullable": true }, + "subdomain": { "type": "string", "nullable": true }, + "verificationError": { "type": "string", "nullable": true }, + "lastCheckedTime": { "type": "string", "nullable": true }, + "dnsRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["MX", "TXT"], + "description": "DNS record type", + "example": "TXT" + }, + "name": { + "type": "string", + "description": "DNS record name", + "example": "mail" + }, + "value": { + "type": "string", + "description": "DNS record value", + "example": "v=spf1 include:amazonses.com ~all" + }, + "ttl": { + "type": "string", + "description": "DNS record TTL", + "example": "Auto" + }, + "priority": { + "type": "string", + "nullable": true, + "description": "DNS record priority", + "example": "10" + }, + "status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "recommended": { + "type": "boolean", + "description": "Whether the record is recommended" + } + }, + "required": ["type", "name", "value", "ttl", "status"] + } + } + }, + "required": [ + "id", + "name", + "teamId", + "status", + "publicKey", + "createdAt", + "updatedAt", + "dnsRecords" + ] + } + } + } + } + } + }, + "delete": { + "parameters": [ + { + "schema": { "type": "number", "nullable": true, "example": 1 }, + "required": false, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Domain deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "success": { "type": "boolean" }, + "message": { "type": "string" } + }, + "required": ["id", "success", "message"] + } + } + } + }, + "403": { + "description": "Forbidden - API key doesn't have access", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + }, + "404": { + "description": "Domain not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + } + } + } + }, + "/v1/emails/{emailId}": { + "get": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 3, + "example": "cuiwqdj74rygf74" + }, + "required": true, + "name": "emailId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Retrieve the email", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "teamId": { "type": "number" }, + "to": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "replyTo": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "cc": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "bcc": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "from": { "type": "string" }, + "subject": { "type": "string" }, + "html": { "type": "string", "nullable": true }, + "text": { "type": "string", "nullable": true }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "emailEvents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "SCHEDULED", + "QUEUED", + "SENT", + "DELIVERY_DELAYED", + "BOUNCED", + "REJECTED", + "RENDERING_FAILURE", + "DELIVERED", + "OPENED", + "CLICKED", + "COMPLAINED", + "FAILED", + "CANCELLED", + "SUPPRESSED" + ] + }, + "createdAt": { "type": "string" }, + "data": { "nullable": true } + }, + "required": ["emailId", "status", "createdAt"] + } + } + }, + "required": [ + "id", + "teamId", + "to", + "from", + "subject", + "html", + "text", + "createdAt", + "updatedAt", + "emailEvents" + ] + } + } + } + } + } + }, + "patch": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 3, + "example": "cuiwqdj74rygf74" + }, + "required": true, + "name": "emailId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "scheduledAt": { "type": "string", "format": "date-time" } + }, + "required": ["scheduledAt"] + } + } + } + }, + "responses": { + "200": { + "description": "Retrieve the user", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "emailId": { "type": "string" } } + } + } + } + } + } + } + }, + "/v1/emails": { + "get": { + "parameters": [ + { + "schema": { "type": "string", "default": "1", "example": "1" }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { "type": "string", "default": "50", "example": "50" }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "format": "date-time", + "example": "2024-01-01T00:00:00Z" + }, + "required": false, + "name": "startDate", + "in": "query" + }, + { + "schema": { + "type": "string", + "format": "date-time", + "example": "2024-01-31T23:59:59Z" + }, + "required": false, + "name": "endDate", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ], + "example": "123" + }, + "required": false, + "name": "domainId", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Retrieve a list of emails", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "to": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "replyTo": { + "anyOf": [ + { "type": "string" }, + { + "type": "array", + "items": { "type": "string" } + }, + { "nullable": true } + ] + }, + "cc": { + "anyOf": [ + { "type": "string" }, + { + "type": "array", + "items": { "type": "string" } + }, + { "nullable": true } + ] + }, + "bcc": { + "anyOf": [ + { "type": "string" }, + { + "type": "array", + "items": { "type": "string" } + }, + { "nullable": true } + ] + }, + "from": { "type": "string" }, + "subject": { "type": "string" }, + "html": { "type": "string", "nullable": true }, + "text": { "type": "string", "nullable": true }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "latestStatus": { + "type": "string", + "nullable": true, + "enum": [ + "SCHEDULED", + "QUEUED", + "SENT", + "DELIVERY_DELAYED", + "BOUNCED", + "REJECTED", + "RENDERING_FAILURE", + "DELIVERED", + "OPENED", + "CLICKED", + "COMPLAINED", + "FAILED", + "CANCELLED", + "SUPPRESSED" + ] + }, + "scheduledAt": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "domainId": { "type": "number", "nullable": true } + }, + "required": [ + "id", + "to", + "from", + "subject", + "html", + "text", + "createdAt", + "updatedAt", + "latestStatus", + "scheduledAt", + "domainId" + ] + } + }, + "count": { "type": "number" } + }, + "required": ["data", "count"] + } + } + } + } + } + }, + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Pass the optional Idempotency-Key header to make the request safe to retry. The key can be up to 256 characters. The server stores the canonical request body and behaves as follows:\n\n- Same key + same request body → returns the original emailId with 200 OK without re-sending.\n- Same key + different request body → returns 409 Conflict with code: NOT_UNIQUE so you can detect the mismatch.\n- Same key while another request is still being processed → returns 409 Conflict; retry after a short delay or once the first request completes.\n\nEntries expire after 24 hours. Use a unique key per logical send (for example, an order or signup ID)." + }, + "required": false, + "name": "Idempotency-Key", + "in": "header" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "from": { "type": "string" }, + "subject": { + "type": "string", + "minLength": 1, + "description": "Optional when templateId is provided" + }, + "templateId": { + "type": "string", + "description": "ID of a template from the dashboard" + }, + "variables": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "replyTo": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "cc": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "bcc": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "text": { + "type": "string", + "nullable": true, + "minLength": 1 + }, + "html": { + "type": "string", + "nullable": true, + "minLength": 1 + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "Custom headers to included with the emails" + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "filename": { "type": "string", "minLength": 1 }, + "content": { "type": "string", "minLength": 1 } + }, + "required": ["filename", "content"] + }, + "maxItems": 10 + }, + "scheduledAt": { "type": "string", "format": "date-time" }, + "inReplyToId": { "type": "string", "nullable": true } + }, + "required": ["to", "from"] + } + } + } + }, + "responses": { + "200": { + "description": "Retrieve the user", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "emailId": { "type": "string" } } + } + } + } + } + } + } + }, + "/v1/emails/batch": { + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Pass the optional Idempotency-Key header to make the request safe to retry. The key can be up to 256 characters. The server stores the canonical request body and behaves as follows:\n\n- Same key + same request body → returns the original emailId with 200 OK without re-sending.\n- Same key + different request body → returns 409 Conflict with code: NOT_UNIQUE so you can detect the mismatch.\n- Same key while another request is still being processed → returns 409 Conflict; retry after a short delay or once the first request completes.\n\nEntries expire after 24 hours. Use a unique key per logical send (for example, an order or signup ID)." + }, + "required": false, + "name": "Idempotency-Key", + "in": "header" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "from": { "type": "string" }, + "subject": { + "type": "string", + "minLength": 1, + "description": "Optional when templateId is provided" + }, + "templateId": { + "type": "string", + "description": "ID of a template from the dashboard" + }, + "variables": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "replyTo": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "cc": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "bcc": { + "anyOf": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } } + ] + }, + "text": { + "type": "string", + "nullable": true, + "minLength": 1 + }, + "html": { + "type": "string", + "nullable": true, + "minLength": 1 + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "Custom headers to included with the emails" + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "filename": { "type": "string", "minLength": 1 }, + "content": { "type": "string", "minLength": 1 } + }, + "required": ["filename", "content"] + }, + "maxItems": 10 + }, + "scheduledAt": { "type": "string", "format": "date-time" }, + "inReplyToId": { "type": "string", "nullable": true } + }, + "required": ["to", "from"] + }, + "maxItems": 100 + } + } + } + }, + "responses": { + "200": { + "description": "List of successfully created email IDs", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { "emailId": { "type": "string" } }, + "required": ["emailId"] + } + } + }, + "required": ["data"] + } + } + } + } + } + } + }, + "/v1/emails/{emailId}/cancel": { + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 3, + "example": "cuiwqdj74rygf74" + }, + "required": true, + "name": "emailId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Retrieve the user", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "emailId": { "type": "string" } } + } + } + } + } + } + } + }, + "/v1/contactBooks": { + "get": { + "responses": { + "200": { + "description": "Retrieve contact books accessible by the API key", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the contact book", + "example": "clx1234567890" + }, + "name": { + "type": "string", + "description": "The name of the contact book", + "example": "Newsletter Subscribers" + }, + "teamId": { + "type": "number", + "description": "The ID of the team", + "example": 1 + }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Custom properties for the contact book", + "example": { "customField1": "value1" } + }, + "emoji": { + "type": "string", + "description": "The emoji associated with the contact book", + "example": "📙" + }, + "createdAt": { + "type": "string", + "description": "The creation timestamp" + }, + "updatedAt": { + "type": "string", + "description": "The last update timestamp" + }, + "_count": { + "type": "object", + "properties": { + "contacts": { + "type": "number", + "description": "The number of contacts in the contact book" + } + } + } + }, + "required": [ + "id", + "name", + "teamId", + "properties", + "emoji", + "createdAt", + "updatedAt" + ] + } + } + } + } + } + } + }, + "post": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { "type": "string", "minLength": 1 }, + "emoji": { "type": "string" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "required": ["name"] + } + } + } + }, + "responses": { + "200": { + "description": "Create a new contact book", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "teamId": { "type": "number" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "emoji": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "id", + "name", + "teamId", + "properties", + "emoji", + "createdAt", + "updatedAt" + ] + } + } + } + } + } + } + }, + "/v1/contactBooks/{contactBookId}": { + "get": { + "parameters": [ + { + "schema": { "type": "string", "example": "clx1234567890" }, + "required": true, + "name": "contactBookId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Retrieve the contact book", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "teamId": { "type": "number" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "emoji": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" }, + "_count": { + "type": "object", + "properties": { + "contacts": { "type": "number" } + } + } + }, + "required": [ + "id", + "name", + "teamId", + "properties", + "emoji", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "403": { + "description": "Forbidden - API key doesn't have access", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + }, + "404": { + "description": "Contact book not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + } + } + }, + "patch": { + "parameters": [ + { + "schema": { "type": "string", "example": "clx1234567890" }, + "required": true, + "name": "contactBookId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { "type": "string", "minLength": 1 }, + "emoji": { "type": "string" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Update the contact book", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "teamId": { "type": "number" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "emoji": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "id", + "name", + "teamId", + "properties", + "emoji", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "403": { + "description": "Forbidden - API key doesn't have access", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + }, + "404": { + "description": "Contact book not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + } + } + }, + "delete": { + "parameters": [ + { + "schema": { "type": "string", "example": "clx1234567890" }, + "required": true, + "name": "contactBookId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Contact book deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "success": { "type": "boolean" }, + "message": { "type": "string" } + }, + "required": ["id", "success", "message"] + } + } + } + }, + "403": { + "description": "Forbidden - API key doesn't have access", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + }, + "404": { + "description": "Contact book not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "error": { "type": "string" } }, + "required": ["error"] + } + } + } + } + } + } + }, + "/v1/contactBooks/{contactBookId}/contacts": { + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 3, + "example": "cuiwqdj74rygf74" + }, + "required": true, + "name": "contactBookId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { "type": "string" }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "subscribed": { "type": "boolean" } + }, + "required": ["email"] + } + } + } + }, + "responses": { + "200": { + "description": "Retrieve the user", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "contactId": { "type": "string" } } + } + } + } + } + } + }, + "get": { + "parameters": [ + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactBookId", + "in": "path" + }, + { + "schema": { "type": "string" }, + "required": false, + "name": "emails", + "in": "query" + }, + { + "schema": { "type": "number" }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { "type": "number" }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { "type": "string" }, + "required": false, + "name": "ids", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Retrieve multiple contacts", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "firstName": { "type": "string", "nullable": true }, + "lastName": { "type": "string", "nullable": true }, + "email": { "type": "string" }, + "subscribed": { "type": "boolean", "default": true }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "contactBookId": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "id", + "email", + "properties", + "contactBookId", + "createdAt", + "updatedAt" + ] + } + } + } + } + } + } + } + }, + "/v1/contactBooks/{contactBookId}/contacts/{contactId}": { + "patch": { + "parameters": [ + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactBookId", + "in": "path" + }, + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "subscribed": { "type": "boolean" } + } + } + } + } + }, + "responses": { + "200": { + "description": "Retrieve the user", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "contactId": { "type": "string" } } + } + } + } + } + } + }, + "get": { + "parameters": [ + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactBookId", + "in": "path" + }, + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Retrieve the contact", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "firstName": { "type": "string", "nullable": true }, + "lastName": { "type": "string", "nullable": true }, + "email": { "type": "string" }, + "subscribed": { "type": "boolean", "default": true }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "contactBookId": { "type": "string" }, + "createdAt": { "type": "string" }, + "updatedAt": { "type": "string" } + }, + "required": [ + "id", + "email", + "properties", + "contactBookId", + "createdAt", + "updatedAt" + ] + } + } + } + } + } + }, + "put": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 3, + "example": "cuiwqdj74rygf74" + }, + "required": true, + "name": "contactBookId", + "in": "path" + }, + { + "schema": { + "type": "string", + "minLength": 3, + "example": "cuiwqdj74rygf74" + }, + "required": true, + "name": "contactId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { "type": "string" }, + "firstName": { "type": "string" }, + "lastName": { "type": "string" }, + "properties": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "subscribed": { "type": "boolean" } + }, + "required": ["email"] + } + } + } + }, + "responses": { + "200": { + "description": "Contact upserted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "contactId": { "type": "string" } }, + "required": ["contactId"] + } + } + } + } + } + }, + "delete": { + "parameters": [ + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactBookId", + "in": "path" + }, + { + "schema": { "type": "string", "example": "cuiwqdj74rygf74" }, + "required": true, + "name": "contactId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Contact deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "success": { "type": "boolean" } }, + "required": ["success"] + } + } + } + } + } + } + }, + "/v1/campaigns": { + "post": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { "type": "string", "minLength": 1 }, + "from": { "type": "string", "minLength": 1 }, + "subject": { "type": "string", "minLength": 1 }, + "previewText": { "type": "string" }, + "contactBookId": { "type": "string", "minLength": 1 }, + "content": { "type": "string", "minLength": 1 }, + "html": { "type": "string", "minLength": 1 }, + "replyTo": { + "anyOf": [ + { "type": "string", "minLength": 1 }, + { + "type": "array", + "items": { "type": "string", "minLength": 1 } + } + ] + }, + "cc": { + "anyOf": [ + { "type": "string", "minLength": 1 }, + { + "type": "array", + "items": { "type": "string", "minLength": 1 } + } + ] + }, + "bcc": { + "anyOf": [ + { "type": "string", "minLength": 1 }, + { + "type": "array", + "items": { "type": "string", "minLength": 1 } + } + ] + }, + "sendNow": { "type": "boolean" }, + "scheduledAt": { + "type": "string", + "description": "Timestamp in ISO 8601 format or natural language (e.g., 'tomorrow 9am', 'next monday 10:30')" + }, + "batchSize": { + "type": "integer", + "minimum": 1, + "maximum": 100000 + } + }, + "required": ["name", "from", "subject", "contactBookId"] + } + } + } + }, + "responses": { + "200": { + "description": "Create a campaign", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "from": { "type": "string" }, + "subject": { "type": "string" }, + "previewText": { "type": "string", "nullable": true }, + "contactBookId": { "type": "string", "nullable": true }, + "html": { "type": "string", "nullable": true }, + "content": { "type": "string", "nullable": true }, + "status": { "type": "string" }, + "scheduledAt": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "batchSize": { "type": "integer" }, + "batchWindowMinutes": { "type": "integer" }, + "total": { "type": "integer" }, + "sent": { "type": "integer" }, + "delivered": { "type": "integer" }, + "opened": { "type": "integer" }, + "clicked": { "type": "integer" }, + "unsubscribed": { "type": "integer" }, + "bounced": { "type": "integer" }, + "hardBounced": { "type": "integer" }, + "complained": { "type": "integer" }, + "replyTo": { + "type": "array", + "items": { "type": "string" } + }, + "cc": { "type": "array", "items": { "type": "string" } }, + "bcc": { "type": "array", "items": { "type": "string" } }, + "createdAt": { "type": "string", "format": "date-time" }, + "updatedAt": { "type": "string", "format": "date-time" } + }, + "required": [ + "id", + "name", + "from", + "subject", + "previewText", + "contactBookId", + "html", + "content", + "status", + "scheduledAt", + "batchSize", + "batchWindowMinutes", + "total", + "sent", + "delivered", + "opened", + "clicked", + "unsubscribed", + "bounced", + "hardBounced", + "complained", + "replyTo", + "cc", + "bcc", + "createdAt", + "updatedAt" + ] + } + } + } + } + } + } + }, + "/v1/campaigns/{campaignId}": { + "get": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "example": "cmp_123" + }, + "required": true, + "name": "campaignId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Get campaign details", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "from": { "type": "string" }, + "subject": { "type": "string" }, + "previewText": { "type": "string", "nullable": true }, + "contactBookId": { "type": "string", "nullable": true }, + "html": { "type": "string", "nullable": true }, + "content": { "type": "string", "nullable": true }, + "status": { "type": "string" }, + "scheduledAt": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "batchSize": { "type": "integer" }, + "batchWindowMinutes": { "type": "integer" }, + "total": { "type": "integer" }, + "sent": { "type": "integer" }, + "delivered": { "type": "integer" }, + "opened": { "type": "integer" }, + "clicked": { "type": "integer" }, + "unsubscribed": { "type": "integer" }, + "bounced": { "type": "integer" }, + "hardBounced": { "type": "integer" }, + "complained": { "type": "integer" }, + "replyTo": { + "type": "array", + "items": { "type": "string" } + }, + "cc": { "type": "array", "items": { "type": "string" } }, + "bcc": { "type": "array", "items": { "type": "string" } }, + "createdAt": { "type": "string", "format": "date-time" }, + "updatedAt": { "type": "string", "format": "date-time" } + }, + "required": [ + "id", + "name", + "from", + "subject", + "previewText", + "contactBookId", + "html", + "content", + "status", + "scheduledAt", + "batchSize", + "batchWindowMinutes", + "total", + "sent", + "delivered", + "opened", + "clicked", + "unsubscribed", + "bounced", + "hardBounced", + "complained", + "replyTo", + "cc", + "bcc", + "createdAt", + "updatedAt" + ] + } + } + } + } + } + } + }, + "/v1/campaigns/{campaignId}/schedule": { + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "example": "cmp_123" + }, + "required": true, + "name": "campaignId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "scheduledAt": { + "type": "string", + "description": "Timestamp in ISO 8601 format or natural language (e.g., 'tomorrow 9am', 'next monday 10:30')" + }, + "batchSize": { + "type": "integer", + "minimum": 1, + "maximum": 100000 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Schedule a campaign", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "success": { "type": "boolean" } }, + "required": ["success"] + } + } + } + } + } + } + }, + "/v1/campaigns/{campaignId}/pause": { + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "example": "cmp_123" + }, + "required": true, + "name": "campaignId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Pause a campaign", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "success": { "type": "boolean" } }, + "required": ["success"] + } + } + } + } + } + } + }, + "/v1/campaigns/{campaignId}/resume": { + "post": { + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "example": "cmp_123" + }, + "required": true, + "name": "campaignId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Resume a campaign", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "success": { "type": "boolean" } }, + "required": ["success"] + } + } + } + } + } + } + } + } } diff --git a/apps/docs/docs.json b/apps/docs/docs.json index a30a757f..75942475 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -64,6 +64,16 @@ "api-reference/emails/cancel-schedule" ] }, + { + "group": "Contact Books", + "pages": [ + "api-reference/contacts/list-contact-books", + "api-reference/contacts/get-contact-book", + "api-reference/contacts/create-contact-book", + "api-reference/contacts/update-contact-book", + "api-reference/contacts/delete-contact-book" + ] + }, { "group": "Contacts", "pages": [ diff --git a/apps/web/src/lib/zod/contact-book-schema.ts b/apps/web/src/lib/zod/contact-book-schema.ts new file mode 100644 index 00000000..d5e0c0c6 --- /dev/null +++ b/apps/web/src/lib/zod/contact-book-schema.ts @@ -0,0 +1,23 @@ +import { z } from "zod"; + +export const ContactBookSchema = z.object({ + id: z + .string() + .openapi({ description: "The ID of the contact book", example: "clx1234567890" }), + name: z + .string() + .openapi({ description: "The name of the contact book", example: "Newsletter Subscribers" }), + teamId: z.number().openapi({ description: "The ID of the team", example: 1 }), + properties: z.record(z.string()).openapi({ + description: "Custom properties for the contact book", + example: { customField1: "value1" }, + }), + emoji: z + .string() + .openapi({ description: "The emoji associated with the contact book", example: "📙" }), + createdAt: z.string().openapi({ description: "The creation timestamp" }), + updatedAt: z.string().openapi({ description: "The last update timestamp" }), + _count: z.object({ + contacts: z.number().openapi({ description: "The number of contacts in the contact book" }), + }).optional(), +}); diff --git a/apps/web/src/server/public-api/api/contacts/create-contact-book.ts b/apps/web/src/server/public-api/api/contacts/create-contact-book.ts new file mode 100644 index 00000000..9ac0a12c --- /dev/null +++ b/apps/web/src/server/public-api/api/contacts/create-contact-book.ts @@ -0,0 +1,65 @@ +import { createRoute, z } from "@hono/zod-openapi"; +import { ContactBookSchema } from "~/lib/zod/contact-book-schema"; +import { PublicAPIApp } from "~/server/public-api/hono"; +import { + createContactBook as createContactBookService, + updateContactBook, +} from "~/server/service/contact-book-service"; + +const route = createRoute({ + method: "post", + path: "/v1/contactBooks", + request: { + body: { + required: true, + content: { + "application/json": { + schema: z.object({ + name: z.string().min(1), + emoji: z.string().optional(), + properties: z.record(z.string()).optional(), + }), + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ContactBookSchema, + }, + }, + description: "Create a new contact book", + }, + }, +}); + +function createContactBook(app: PublicAPIApp) { + app.openapi(route, async (c) => { + const team = c.var.team; + const body = c.req.valid("json"); + + const contactBook = await createContactBookService(team.id, body.name); + + // Update emoji and properties if provided + if (body.emoji || body.properties) { + const updated = await updateContactBook(contactBook.id, { + emoji: body.emoji, + properties: body.properties, + }); + + return c.json({ + ...updated, + properties: updated.properties as Record, + }); + } + + return c.json({ + ...contactBook, + properties: contactBook.properties as Record, + }); + }); +} + +export default createContactBook; diff --git a/apps/web/src/server/public-api/api/contacts/delete-contact-book.ts b/apps/web/src/server/public-api/api/contacts/delete-contact-book.ts new file mode 100644 index 00000000..1b0c3378 --- /dev/null +++ b/apps/web/src/server/public-api/api/contacts/delete-contact-book.ts @@ -0,0 +1,73 @@ +import { createRoute, z } from "@hono/zod-openapi"; +import { PublicAPIApp } from "../../hono"; +import { deleteContactBook as deleteContactBookService } from "~/server/service/contact-book-service"; +import { getContactBook } from "../../api-utils"; + +const route = createRoute({ + method: "delete", + path: "/v1/contactBooks/{contactBookId}", + request: { + params: z.object({ + contactBookId: z.string().openapi({ + param: { + name: "contactBookId", + in: "path", + }, + example: "clx1234567890", + }), + }), + }, + responses: { + 200: { + content: { + "application/json": { + schema: z.object({ + id: z.string(), + success: z.boolean(), + message: z.string(), + }), + }, + }, + description: "Contact book deleted successfully", + }, + 403: { + content: { + "application/json": { + schema: z.object({ + error: z.string(), + }), + }, + }, + description: "Forbidden - API key doesn't have access", + }, + 404: { + content: { + "application/json": { + schema: z.object({ + error: z.string(), + }), + }, + }, + description: "Contact book not found", + }, + }, +}); + +function deleteContactBook(app: PublicAPIApp) { + app.openapi(route, async (c) => { + const team = c.var.team; + const contactBookId = c.req.valid("param").contactBookId; + + await getContactBook(c, team.id); + + const deletedContactBook = await deleteContactBookService(contactBookId); + + return c.json({ + id: deletedContactBook.id, + success: true, + message: "Contact book deleted successfully", + }); + }); +} + +export default deleteContactBook; diff --git a/apps/web/src/server/public-api/api/contacts/get-contact-book.ts b/apps/web/src/server/public-api/api/contacts/get-contact-book.ts new file mode 100644 index 00000000..b4149241 --- /dev/null +++ b/apps/web/src/server/public-api/api/contacts/get-contact-book.ts @@ -0,0 +1,85 @@ +import { createRoute, z } from "@hono/zod-openapi"; +import { ContactBookSchema } from "~/lib/zod/contact-book-schema"; +import { PublicAPIApp } from "~/server/public-api/hono"; +import { db } from "~/server/db"; +import { UnsendApiError } from "../../api-error"; + +const route = createRoute({ + method: "get", + path: "/v1/contactBooks/{contactBookId}", + request: { + params: z.object({ + contactBookId: z.string().openapi({ + param: { + name: "contactBookId", + in: "path", + }, + example: "clx1234567890", + }), + }), + }, + responses: { + 200: { + content: { + "application/json": { + schema: ContactBookSchema, + }, + }, + description: "Retrieve the contact book", + }, + 403: { + content: { + "application/json": { + schema: z.object({ + error: z.string(), + }), + }, + }, + description: + "Forbidden - API key doesn't have access to this contact book", + }, + 404: { + content: { + "application/json": { + schema: z.object({ + error: z.string(), + }), + }, + }, + description: "Contact book not found", + }, + }, +}); + +function getContactBook(app: PublicAPIApp) { + app.openapi(route, async (c) => { + const team = c.var.team; + const contactBookId = c.req.valid("param").contactBookId; + + const contactBook = await db.contactBook.findFirst({ + where: { + id: contactBookId, + teamId: team.id, + }, + include: { + _count: { + select: { contacts: true }, + }, + }, + }); + + if (!contactBook) { + throw new UnsendApiError({ + code: "NOT_FOUND", + message: "Contact book not found", + }); + } + + return c.json({ + ...contactBook, + properties: contactBook.properties as Record, + }); + }); +} + +export default getContactBook; diff --git a/apps/web/src/server/public-api/api/contacts/get-contact-books.ts b/apps/web/src/server/public-api/api/contacts/get-contact-books.ts new file mode 100644 index 00000000..cc164a93 --- /dev/null +++ b/apps/web/src/server/public-api/api/contacts/get-contact-books.ts @@ -0,0 +1,37 @@ +import { createRoute, z } from "@hono/zod-openapi"; +import { ContactBookSchema } from "~/lib/zod/contact-book-schema"; +import { PublicAPIApp } from "~/server/public-api/hono"; +import { getContactBooks as getContactBooksService } from "~/server/service/contact-book-service"; + +const route = createRoute({ + method: "get", + path: "/v1/contactBooks", + responses: { + 200: { + content: { + "application/json": { + schema: z.array(ContactBookSchema), + }, + }, + description: "Retrieve contact books accessible by the API key", + }, + }, +}); + +function getContactBooks(app: PublicAPIApp) { + app.openapi(route, async (c) => { + const team = c.var.team; + + const contactBooks = await getContactBooksService(team.id); + + // Ensure properties is a Record + const sanitizedContactBooks = contactBooks.map((contactBook) => ({ + ...contactBook, + properties: contactBook.properties as Record, + })); + + return c.json(sanitizedContactBooks); + }); +} + +export default getContactBooks; diff --git a/apps/web/src/server/public-api/api/contacts/update-contact-book.ts b/apps/web/src/server/public-api/api/contacts/update-contact-book.ts new file mode 100644 index 00000000..0adc2847 --- /dev/null +++ b/apps/web/src/server/public-api/api/contacts/update-contact-book.ts @@ -0,0 +1,83 @@ +import { createRoute, z } from "@hono/zod-openapi"; +import { ContactBookSchema } from "~/lib/zod/contact-book-schema"; +import { PublicAPIApp } from "~/server/public-api/hono"; +import { updateContactBook as updateContactBookService } from "~/server/service/contact-book-service"; +import { getContactBook } from "../../api-utils"; + +const route = createRoute({ + method: "patch", + path: "/v1/contactBooks/{contactBookId}", + request: { + params: z.object({ + contactBookId: z.string().openapi({ + param: { + name: "contactBookId", + in: "path", + }, + example: "clx1234567890", + }), + }), + body: { + required: true, + content: { + "application/json": { + schema: z.object({ + name: z.string().min(1).optional(), + emoji: z.string().optional(), + properties: z.record(z.string()).optional(), + }), + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ContactBookSchema, + }, + }, + description: "Update the contact book", + }, + 403: { + content: { + "application/json": { + schema: z.object({ + error: z.string(), + }), + }, + }, + description: + "Forbidden - API key doesn't have access to this contact book", + }, + 404: { + content: { + "application/json": { + schema: z.object({ + error: z.string(), + }), + }, + }, + description: "Contact book not found", + }, + }, +}); + +function updateContactBook(app: PublicAPIApp) { + app.openapi(route, async (c) => { + const team = c.var.team; + const contactBookId = c.req.valid("param").contactBookId; + const body = c.req.valid("json"); + + await getContactBook(c, team.id); + + const updated = await updateContactBookService(contactBookId, body); + + return c.json({ + ...updated, + properties: updated.properties as Record, + }); + }); +} + +export default updateContactBook; diff --git a/apps/web/src/server/public-api/index.ts b/apps/web/src/server/public-api/index.ts index 9f4afdda..0a57bbb9 100644 --- a/apps/web/src/server/public-api/index.ts +++ b/apps/web/src/server/public-api/index.ts @@ -22,6 +22,11 @@ import getCampaigns from "./api/campaigns/get-campaigns"; import scheduleCampaign from "./api/campaigns/schedule-campaign"; import pauseCampaign from "./api/campaigns/pause-campaign"; import resumeCampaign from "./api/campaigns/resume-campaign"; +import getContactBooks from "./api/contacts/get-contact-books"; +import createContactBook from "./api/contacts/create-contact-book"; +import getContactBook from "./api/contacts/get-contact-book"; +import updateContactBook from "./api/contacts/update-contact-book"; +import deleteContactBook from "./api/contacts/delete-contact-book"; export const app = getApp(); @@ -48,6 +53,13 @@ getContacts(app); upsertContact(app); deleteContact(app); +/**Contact Book related APIs */ +getContactBooks(app); +createContactBook(app); +getContactBook(app); +updateContactBook(app); +deleteContactBook(app); + /**Campaign related APIs */ createCampaign(app); getCampaign(app);