{
  "openapi": "3.0.3",
  "info": {
    "title": "NewHope SMS API",
    "version": "1.0.0",
    "description": "RESTful HTTP API for sending single and bulk SMS messages, OTP verification, delivery reports, and sender ID management. Targeting businesses, schools, hospitals, churches, NGOs, and developers in Tanzania.\n\n**Base URL:** https://sms.newhope.co.tz/v1\n\n**Authentication:** ApiKey scheme \u2014 requires BOTH API Key and Secret Key.\n\nHeader format: `Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY`\n\n- API Key starts with `nhk_` (obtain from dashboard \u2192 API Keys)\n- Secret Key starts with `nhs_` (shown once at generation \u2014 save immediately)\n\n**Rate limit:** 200 requests per minute per API key.\n\n**Phone format:** E.164 (+255XXXXXXXXX for Tanzania).\n\n**Support:** support@newhope.co.tz | https://sms.newhope.co.tz",
    "contact": {
      "name": "NewHope SMS Support",
      "email": "support@newhope.co.tz",
      "url": "https://sms.newhope.co.tz"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://sms.newhope.co.tz/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Authenticate using BOTH your API Key and Secret Key in the Authorization header.\n\nHeader format:\n    Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY\n\n- YOUR_API_KEY starts with nhk_ (obtained from dashboard \u2192 API Keys)\n- YOUR_SECRET_KEY starts with nhs_ (shown once at key generation \u2014 save immediately)\n\nExample:\n    Authorization: ApiKey nhk_abc123xyz:nhs_def456uvw\n\nThree key types available:\n- General: full access to all endpoints\n- SMS Sender: restricted to sending SMS with one specific sender ID\n- OTP: restricted to /otp/send/ and /otp/verify/ only"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Insufficient SMS credits."
          },
          "detail": {
            "type": "string",
            "example": "Authentication credentials were not provided."
          }
        }
      },
      "ContactList": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Customers Q1 2026"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "contacts_count": {
            "type": "integer",
            "example": 1240
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "phone_number": {
            "type": "string",
            "example": "+255712345678"
          },
          "first_name": {
            "type": "string",
            "nullable": true
          },
          "last_name": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SMSMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sent",
              "delivered",
              "failed"
            ]
          },
          "recipient": {
            "type": "string",
            "example": "+255712345678"
          },
          "message_text": {
            "type": "string"
          },
          "sender_id_name": {
            "type": "string",
            "example": "NewHope"
          },
          "gateway_message_id": {
            "type": "string"
          },
          "cost": {
            "type": "string",
            "example": "20.00"
          },
          "segments": {
            "type": "integer",
            "example": 1
          },
          "sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SenderID": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "sender_name": {
            "type": "string",
            "example": "NewHope",
            "maxLength": 11
          },
          "status": {
            "type": "string",
            "enum": [
              "awaiting_payment",
              "pending",
              "under_review",
              "approved",
              "rejected"
            ]
          },
          "is_default": {
            "type": "boolean"
          },
          "approved_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejection_reason": {
            "type": "string",
            "nullable": true
          }
        }
      }
    }
  },
  "paths": {
    "/sms/send/": {
      "post": {
        "operationId": "sendSMS",
        "summary": "Send a single SMS",
        "description": "Send one SMS message to one recipient. Consumes 1 credit per SMS segment (1 segment = up to 160 GSM-7 characters or 70 Unicode characters). Returns HTTP 402 if insufficient credits.",
        "tags": [
          "SMS"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "recipient",
                  "message"
                ],
                "properties": {
                  "recipient": {
                    "type": "string",
                    "example": "+255712345678",
                    "description": "E.164 phone number. Tanzania numbers: +255XXXXXXXXX"
                  },
                  "message": {
                    "type": "string",
                    "example": "Hello from NewHope SMS!",
                    "description": "SMS body. Max 918 characters (6 segments)."
                  },
                  "sender_id": {
                    "type": "string",
                    "example": "NewHope",
                    "description": "Approved sender name (max 11 chars). Uses account default if omitted."
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "example": "https://yourapp.com/dlr",
                    "description": "URL to receive delivery receipt POSTs. Must respond 200."
                  },
                  "schedule_at": {
                    "type": "string",
                    "format": "date-time",
                    "example": "2026-06-10T09:00:00Z",
                    "description": "ISO 8601 future datetime to schedule delivery."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "SMS sent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SMSMessage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient SMS credits \u2014 top up at https://sms.newhope.co.tz/purchases",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Account restricted or API key type not permitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (200 req/min)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sms/send-batch/": {
      "post": {
        "operationId": "sendBulkSMS",
        "summary": "Send bulk SMS to a contact list",
        "description": "Send the same message to every contact in a contact list in one request. Credits consumed = number_of_contacts \u00d7 sms_segments. Get contact list UUIDs from GET /contacts/lists/",
        "tags": [
          "SMS"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "contact_list_id",
                  "message"
                ],
                "properties": {
                  "contact_list_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID of the contact list to send to."
                  },
                  "message": {
                    "type": "string",
                    "description": "SMS message body."
                  },
                  "sender_id": {
                    "type": "string",
                    "example": "NewHope",
                    "description": "Approved sender name. Uses account default if omitted."
                  },
                  "schedule_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Optional future send time."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued_count": {
                      "type": "integer",
                      "example": 250
                    },
                    "status": {
                      "type": "string",
                      "example": "queued"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters"
          },
          "402": {
            "description": "Insufficient SMS credits"
          }
        }
      }
    },
    "/sms/reports/": {
      "get": {
        "operationId": "listSMSReports",
        "summary": "List sent messages and delivery statuses",
        "description": "Paginated list of your SMS messages with delivery status.",
        "tags": [
          "SMS"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "sent",
                "delivered",
                "failed"
              ]
            },
            "description": "Filter by delivery status."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            },
            "description": "Page number."
          },
          {
            "name": "page_size",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Results per page (max 100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SMSMessage"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/sender-ids/": {
      "get": {
        "operationId": "listSenderIDs",
        "summary": "List your sender IDs",
        "description": "Returns all sender IDs on your account. Only sender IDs with status 'approved' can be used for sending SMS. New sender IDs must be requested via the dashboard and require a non-refundable application fee (TZS 5,000) plus compliance review (1\u20132 business days). Sender ID names: max 11 characters, letters and numbers only, no spaces.",
        "tags": [
          "Sender IDs"
        ],
        "responses": {
          "200": {
            "description": "List of sender IDs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SenderID"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/otp/send/": {
      "post": {
        "operationId": "sendOTP",
        "summary": "Send a 6-digit OTP via SMS",
        "description": "Sends a one-time password to a phone number. OTP expires after 5 minutes (300 seconds). Requires OTP or General API key.",
        "tags": [
          "OTP"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "example": "+255712345678",
                    "description": "Recipient phone in E.164 format."
                  },
                  "sender_id": {
                    "type": "string",
                    "example": "NewHope",
                    "description": "Sender name. Uses account default if omitted."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OTP sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Use this to verify the OTP."
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 300,
                      "description": "Seconds until OTP expires."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/otp/verify/": {
      "post": {
        "operationId": "verifyOTP",
        "summary": "Verify an OTP",
        "description": "Check the 6-digit code submitted by the user against the request_id from /otp/send/. Returns { valid: true } on success.",
        "tags": [
          "OTP"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "request_id",
                  "otp"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Returned by /otp/send/"
                  },
                  "otp": {
                    "type": "string",
                    "example": "847291",
                    "description": "6-digit code entered by the user."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/sms/{sms_id}/status/": {
      "get": {
        "operationId": "getSMSStatus",
        "summary": "Get delivery status of a specific SMS",
        "description": "Check the current delivery status of a message by its ID (returned by /sms/send/).",
        "tags": [
          "SMS"
        ],
        "parameters": [
          {
            "name": "sms_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The SMS message ID returned by /sms/send/"
          }
        ],
        "responses": {
          "200": {
            "description": "SMS status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "sent",
                        "delivered",
                        "failed"
                      ]
                    },
                    "sent_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "delivered_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Message not found"
          }
        }
      }
    },
    "/contacts/": {
      "get": {
        "operationId": "listContactLists",
        "summary": "List all contact lists",
        "description": "Returns all contact lists for your account. Use the list UUID in /sms/send-batch/ \u2192 contact_list_id.",
        "tags": [
          "Contact Lists"
        ],
        "responses": {
          "200": {
            "description": "Array of contact lists",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ContactList"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createContactList",
        "summary": "Create a contact list",
        "description": "Create a new named contact list for bulk SMS campaigns.",
        "tags": [
          "Contact Lists"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "My Customers",
                    "description": "Name for the contact list."
                  },
                  "description": {
                    "type": "string",
                    "description": "Optional description."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Contact list created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters"
          }
        }
      }
    },
    "/contacts/{list_id}/": {
      "get": {
        "operationId": "getContactList",
        "summary": "Get a contact list",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contact list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactList"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateContactList",
        "summary": "Update a contact list name/description",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated contact list"
          }
        }
      },
      "delete": {
        "operationId": "deleteContactList",
        "summary": "Delete a contact list",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          }
        }
      }
    },
    "/contacts/{list_id}/contacts/": {
      "get": {
        "operationId": "listContacts",
        "summary": "List contacts in a list",
        "description": "Paginated list of contacts in a specific contact list.",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search by phone, first name, or last name."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of contacts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "addContact",
        "summary": "Add a single contact to a list",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone_number"
                ],
                "properties": {
                  "phone_number": {
                    "type": "string",
                    "example": "+255712345678"
                  },
                  "first_name": {
                    "type": "string"
                  },
                  "last_name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Contact added",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          }
        }
      }
    },
    "/contacts/{list_id}/contacts/{contact_id}/": {
      "delete": {
        "operationId": "removeContact",
        "summary": "Remove a contact from a list",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "contact_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Contact removed"
          }
        }
      }
    },
    "/contacts/{list_id}/import/": {
      "post": {
        "operationId": "importContacts",
        "summary": "Bulk import contacts from CSV",
        "description": "Upload a CSV file to import many contacts at once. Use multipart/form-data (not JSON). CSV must have a 'phone_number' column header. Optional columns: first_name, last_name.",
        "tags": [
          "Contact Lists"
        ],
        "parameters": [
          {
            "name": "list_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "CSV file with phone_number, first_name (optional), last_name (optional) columns."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "imported": {
                      "type": "integer",
                      "example": 1200
                    },
                    "skipped": {
                      "type": "integer",
                      "example": 15
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "Row 5: invalid phone number"
                      ]
                    },
                    "total_in_list": {
                      "type": "integer",
                      "example": 2440
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "SMS",
      "description": "Send single and bulk SMS messages, check status, view delivery reports."
    },
    {
      "name": "Contact Lists",
      "description": "Manage contact lists and contacts for bulk SMS campaigns."
    },
    {
      "name": "Sender IDs",
      "description": "Manage approved sender names for your account."
    },
    {
      "name": "OTP",
      "description": "Send and verify one-time passwords via SMS."
    }
  ]
}