{
  "openapi": "3.1.1",
  "info": {
    "title": "Sendery API",
    "version": "1.0.0",
    "description": "Send published transactional email templates and retrieve delivery status."
  },
  "servers": [
    {
      "url": "https://sendery.co/api/v1"
    }
  ],
  "security": [
    {
      "projectKey": []
    }
  ],
  "paths": {
    "/emails": {
      "post": {
        "operationId": "sendEmail",
        "summary": "Send a template email",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Use one key per email. Retry with the same recipient, template, data, and locale to return the original ID and current status. A changed payload returns 409. Keys are scoped to the project and retained while the message record exists. Omitting this header creates a new email for each accepted request.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_.:-]+$"
            },
            "example": "welcome-123"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmail"
              },
              "example": {
                "to": "alex@example.com",
                "template": "welcome",
                "data": {
                  "name": "Alex",
                  "action_url": "https://example.com/start"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching accepted request: returns the original ID and current status without creating another email.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                },
                "example": {
                  "id": "35a6b227-1748-412e-8651-430492201670",
                  "status": "delivered"
                }
              }
            }
          },
          "202": {
            "description": "Accepted for sending, not yet delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                },
                "example": {
                  "id": "35a6b227-1748-412e-8651-430492201670",
                  "status": "queued"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, invalid, or revoked. Use an active project key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "invalid_api_key",
                  "message": "The API key is missing, invalid, or revoked."
                }
              }
            }
          },
          "402": {
            "description": "Complete the payment in workspace Billing before sending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "payment_required",
                  "message": "Complete the payment in workspace Billing before sending.."
                }
              }
            }
          },
          "403": {
            "description": "Check the workspace status, sending plan, and access to the requested feature.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "access_denied",
                  "message": "Check the workspace status, sending plan, and access to the requested feature.."
                }
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "not_found",
                  "message": "No email with this ID is accessible using this project key.."
                }
              }
            },
            "description": "No email with this ID is accessible using this project key."
          },
          "409": {
            "description": "This key was already used with a different payload. Restore the original payload for a retry; use a new key for a new email.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "idempotency_conflict",
                  "message": "This key was already used with a different payload."
                }
              }
            }
          },
          "413": {
            "description": "The JSON request body exceeds 65,536 bytes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "request_too_large",
                  "message": "The JSON request body exceeds 65,536 bytes.."
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, missing template or published language, invalid template variables, or incomplete sending setup. Inspect message and any field errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "validation_error",
                  "message": "The given data was invalid.",
                  "errors": {
                    "to": [
                      "The to field must be a valid email address."
                    ]
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Honor Retry-After before retrying. For sending, email_capacity_exceeded instead requires enabling automatic email purchases or upgrading in Billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rateLimit": {
                    "value": {
                      "code": "rate_limited",
                      "message": "Too Many Attempts."
                    }
                  },
                  "capacity": {
                    "value": {
                      "code": "email_capacity_exceeded",
                      "message": "Email capacity has been reached. Enable automatic email purchases or upgrade your plan in workspace Billing to continue sending."
                    }
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds for rate_limited responses. Not included for email_capacity_exceeded.",
                "schema": {
                  "type": "integer",
                  "minimum": 0,
                  "example": 30
                }
              }
            }
          },
          "500": {
            "description": "Temporary server failure. Retry after a delay with the same key and payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Temporary server failure."
                }
              }
            }
          },
          "502": {
            "description": "Temporary gateway failure. Retry after a delay with the same key and payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Temporary gateway failure."
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable. Retry after a delay with the same key and payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Service temporarily unavailable."
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout. Retry after a delay with the same key and payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Gateway timeout."
                }
              }
            }
          }
        },
        "description": "Send to one recipient using a published template. Configure the sender in the project and the subject and body in the template. The JSON request body must not exceed 65,536 bytes."
      }
    },
    "/emails/{id}": {
      "get": {
        "operationId": "getEmail",
        "summary": "Get delivery status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The message ID returned by a send request.",
            "example": "35a6b227-1748-412e-8651-430492201670"
          }
        ],
        "responses": {
          "200": {
            "description": "Current message state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Email"
                },
                "example": {
                  "id": "35a6b227-1748-412e-8651-430492201670",
                  "status": "delivered",
                  "error_code": null,
                  "created_at": "2026-09-27T06:00:00.000000Z",
                  "submitted_at": "2026-09-27T06:00:01.000000Z"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, invalid, or revoked. Use an active project key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "invalid_api_key",
                  "message": "The API key is missing, invalid, or revoked."
                }
              }
            }
          },
          "403": {
            "description": "Check the workspace status, sending plan, and access to the requested feature.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "access_denied",
                  "message": "Check the workspace status, sending plan, and access to the requested feature.."
                }
              }
            }
          },
          "404": {
            "description": "No email with this ID is accessible using this project key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "not_found",
                  "message": "No email with this ID is accessible using this project key.."
                }
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "request_too_large",
                  "message": "The JSON request body exceeds 65,536 bytes.."
                }
              }
            },
            "description": "The JSON request body exceeds 65,536 bytes."
          },
          "429": {
            "description": "Rate limit reached. Wait for the delay in Retry-After before requesting status again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "rate_limited",
                  "message": "Too Many Attempts."
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds for rate_limited responses. Not included for email_capacity_exceeded.",
                "schema": {
                  "type": "integer",
                  "minimum": 0,
                  "example": 30
                }
              }
            }
          },
          "500": {
            "description": "Temporary server failure. Retry after a delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Temporary server failure."
                }
              }
            }
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Temporary gateway failure."
                }
              }
            },
            "description": "Temporary gateway failure. Retry after a delay."
          },
          "503": {
            "description": "Service temporarily unavailable. Retry after a delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Service temporarily unavailable."
                }
              }
            }
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "server_error",
                  "message": "Gateway timeout."
                }
              }
            },
            "description": "Gateway timeout. Retry after a delay."
          }
        },
        "description": "Retrieve status using a key from the project that sent the email. Recipient details, template variables, and email content are not returned. Sending and status requests share a rate limit of 120 requests per minute per project; requests are also limited to 600 per minute per IP address."
      }
    }
  },
  "components": {
    "securitySchemes": {
      "projectKey": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "SendEmail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "to",
          "template",
          "data"
        ],
        "properties": {
          "to": {
            "type": "string",
            "format": "email",
            "maxLength": 255,
            "description": "One recipient email address."
          },
          "template": {
            "type": "string",
            "maxLength": 100,
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "minLength": 1,
            "description": "Key of a template in the API key’s project."
          },
          "data": {
            "type": "object",
            "maxProperties": 50,
            "additionalProperties": {
              "oneOf": [
                {
                  "type": [
                    "string",
                    "number",
                    "boolean"
                  ]
                },
                {
                  "type": "array",
                  "maxItems": 100,
                  "items": {
                    "type": "object",
                    "maxProperties": 10,
                    "additionalProperties": {
                      "type": [
                        "string",
                        "number",
                        "boolean"
                      ]
                    }
                  }
                }
              ]
            },
            "description": "Required object of named variables; use {} if no variables are needed. Scalar values are limited to 10,000 bytes and array cell values to 2,000 bytes. Null and nested objects are not supported. Line Items blocks require 1–100 item rows with name, quantity, and amount, and 0–20 adjustment rows with label and amount. Calculate and format amounts in your application. The entire JSON request body must not exceed 65,536 bytes."
          },
          "locale": {
            "type": "string",
            "description": "Case-insensitive published language tag. Omit to use the template’s default. Non-default languages require template translations on the plan. An unavailable publication returns 422; fr-ca does not fall back to fr.",
            "maxLength": 63,
            "pattern": "^[a-zA-Z]{2,8}(?:-[a-zA-Z0-9]{1,8})*$",
            "examples": [
              "ja",
              "en-gb"
            ]
          }
        }
      },
      "Receipt": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "simulated",
              "submitted",
              "delayed",
              "failed",
              "delivered",
              "bounced",
              "complained",
              "canceled"
            ],
            "description": "Current status. delivered confirms acceptance by the recipient server, not a read. simulated means no live email was delivered. canceled means the template was deleted before sending. Later delivery events can update the status."
          }
        }
      },
      "Email": {
        "type": "object",
        "required": [
          "id",
          "status",
          "error_code",
          "created_at",
          "submitted_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "simulated",
              "submitted",
              "delayed",
              "failed",
              "delivered",
              "bounced",
              "complained",
              "canceled"
            ],
            "description": "Current status. delivered confirms acceptance by the recipient server, not a read. simulated means no live email was delivered. canceled means the template was deleted before sending. Later delivery events can update the status."
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Failure reason, or null. Examples include delivery_unknown, delivery_expired, account_inactive, provider_error, and template_deleted. Bounce or complaint reasons may contain values from the sending provider."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time the email was accepted, in UTC."
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Submission time in UTC, or null before submission. Also set when a sandbox send completes."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Machine-readable code for request errors. This is separate from an email’s delivery error_code."
          },
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "description": "Field-level validation errors, when available."
          }
        }
      }
    }
  }
}
