openapi: 3.1.0
info:
  title: VanceTel API
  version: "1.0.0"
  description: >
    Programmable softphone, SMS and voice API. Send SMS/MMS, create and sync
    contacts, and subscribe to webhooks for inbound messages, delivery receipts
    and call events. The API is write- and event-oriented; the softphone app
    surfaces full conversation and call history in real time.
  contact:
    name: VanceTel
    url: https://api.netexem.com
servers:
  - url: https://api.netexem.com/v1
    description: Production
tags:
  - name: Messages
    description: Send and look up SMS / MMS messages.
  - name: Contacts
    description: Create and manage contacts.
  - name: Webhooks
    description: Register endpoints to receive signed event deliveries.
security:
  - bearerAuth: []
paths:
  /messages:
    post:
      tags: [Messages]
      summary: Send a message
      description: Send an SMS or MMS from one of your numbers. The message also appears in the softphone inbox.
      operationId: sendMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NewMessage"
            examples:
              sms:
                summary: Simple SMS
                value:
                  from: "+15551234567"
                  to: "+15557654321"
                  body: "Your appointment is confirmed for 2pm — reply STOP to opt out."
      responses:
        "201":
          description: Message accepted for delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Message"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
  /messages/{id}:
    get:
      tags: [Messages]
      summary: Retrieve a message
      operationId: getMessage
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: The message.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Message"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /contacts:
    post:
      tags: [Contacts]
      summary: Create a contact
      description: Create a contact on the fly. Adds the person to the shared softphone directory for caller-ID resolution.
      operationId: createContact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NewContact"
            examples:
              fromForm:
                summary: From a website form
                value:
                  name: "Jane Doe"
                  phone: "+15557654321"
                  email: "jane@example.com"
                  company: "Acme Co"
      responses:
        "201":
          description: Contact created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
  /contacts/{id}:
    get:
      tags: [Contacts]
      summary: Retrieve a contact
      operationId: getContact
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: The contact.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"
        "404":
          $ref: "#/components/responses/NotFound"
  /webhook_endpoints:
    post:
      tags: [Webhooks]
      summary: Create a webhook endpoint
      description: Register an HTTPS URL and the event types to deliver. Returns a signing secret (whsec_…) used to verify deliveries.
      operationId: createWebhookEndpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url:
                  type: string
                  format: uri
                  example: "https://app.example.com/hooks/netexem"
                events:
                  type: array
                  items: { type: string }
                  example: ["message.received", "message.delivered", "message.failed", "call.completed", "call.missed", "contact.created"]
      responses:
        "201":
          description: Endpoint created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string, example: "whe_01h…" }
                  url: { type: string }
                  events: { type: array, items: { type: string } }
                  secret: { type: string, example: "whsec_…" }
        "401":
          $ref: "#/components/responses/Unauthorized"
webhooks:
  messageReceived:
    post:
      summary: message.received
      description: >
        Example inbound webhook. Full event catalog: message.received,
        message.delivered, message.failed, call.completed, call.missed,
        contact.created. Every delivery is HMAC-SHA256 signed — the
        X-Netexem-Signature header is t=<unix>,v1=<hex> computed over t + "." + raw body.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEvent"
      responses:
        "200":
          description: Return 2xx to acknowledge. Non-2xx is retried with backoff.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: "Send your API key as: Authorization: Bearer sk_live_…"
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    ValidationError:
      description: The request failed validation.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    NewMessage:
      type: object
      required: [from, to, body]
      properties:
        from:
          type: string
          description: An E.164 number you own.
          example: "+15551234567"
        to:
          type: string
          description: Recipient in E.164 format.
          example: "+15557654321"
        body:
          type: string
          description: Message text.
        mediaUrls:
          type: array
          description: Optional MMS media URLs.
          items: { type: string, format: uri }
    Message:
      type: object
      properties:
        id: { type: string, example: "msg_01h…" }
        from: { type: string }
        to: { type: string }
        body: { type: string }
        direction: { type: string, enum: [outbound, inbound] }
        status:
          type: string
          enum: [queued, sent, delivered, failed]
        createdAt: { type: string, format: date-time }
    NewContact:
      type: object
      required: [phone]
      properties:
        name: { type: string }
        phone: { type: string, example: "+15557654321" }
        email: { type: string, format: email }
        company: { type: string }
    Contact:
      type: object
      properties:
        id: { type: string, example: "ct_01h…" }
        name: { type: string }
        phone: { type: string }
        email: { type: string }
        company: { type: string }
        createdAt: { type: string, format: date-time }
    WebhookEvent:
      type: object
      properties:
        type: { type: string, example: "message.received" }
        createdAt: { type: string, format: date-time }
        data: { type: object, additionalProperties: true }
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            type: { type: string, example: "invalid_request" }
            message: { type: string }
