{
  "openapi": "3.1.0",
  "info": {
    "title": "DeepChat developer API",
    "version": "1.0.0",
    "description": "Send and receive DeepChat messages from your own software. Hosted mode: DeepChat's servers hold the encryption keys for your number and send on your behalf. Get a key in the app under Settings > Developer. Test keys (dc_test_...) use a sandbox and never reach a real phone."
  },
  "servers": [
    {
      "url": "https://api.deepchat.in"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Messages"
    },
    {
      "name": "Files",
      "description": "Images and documents (hosted mode)"
    },
    {
      "name": "Account"
    },
    {
      "name": "Webhook"
    },
    {
      "name": "Sandbox",
      "description": "Test keys only"
    },
    {
      "name": "Keys",
      "description": "Used by the app. Authenticated with the user's session, not an API key."
    }
  ],
  "paths": {
    "/v1/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Your number, plan, limits and usage",
        "responses": {
          "200": {
            "description": "The account behind this key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Send a text message",
        "description": "A message is exactly one of: text (with up to 3 optional buttons), an image, or a document. The first message to a number arrives as a message request. Until the person accepts, further messages are refused with 409. Once they block you, 403.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted for delivery",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The recipient blocked you (blocked_by_recipient), or the key cannot do this",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "That number is not on DeepChat, or the uploaded file does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "awaiting_acceptance: the recipient has not accepted your first message yet. Or the key is in SDK mode.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit, monthly allowance or new-recipient limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "File sending is not enabled on this server",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{id}": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Status of a message you sent",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "msg_8f2a1c0d9b3e4a57"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The message",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such message (messages of other keys are never revealed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts/{number}": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Is this number on DeepChat?",
        "description": "At most 100 lookups an hour per key.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "+919876543210"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Whether the number can receive messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "number": {
                      "type": "string"
                    },
                    "on_deepchat": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "description": "Too many lookups",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook": {
      "put": {
        "tags": [
          "Webhook"
        ],
        "summary": "Set where DeepChat sends events",
        "description": "The URL must be https and public (internal addresses are refused). The signing secret is returned only when it is created or rotated.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSettings"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSaved"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "tags": [
          "Webhook"
        ],
        "summary": "Current webhook settings (never the secret)",
        "responses": {
          "200": {
            "description": "Settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No webhook set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sandbox/inbox": {
      "get": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Messages your test key sent",
        "responses": {
          "200": {
            "description": "The last 100",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "to": {
                            "type": "string"
                          },
                          "text": {
                            "type": "string"
                          },
                          "at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Not a test key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sandbox/receive": {
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Pretend a user wrote to you",
        "description": "Triggers a message.received webhook so you can test your handler.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "text"
                ],
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Event queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Not a test key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/developer/keys": {
      "get": {
        "tags": [
          "Keys"
        ],
        "summary": "List your keys (prefix only)",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Keys",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/KeyInfo"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Create a key (shown once)",
        "security": [
          {
            "session": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "hosting": {
                    "enum": [
                      "hosted",
                      "sdk"
                    ]
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 60
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string"
                    },
                    "info": {
                      "$ref": "#/components/schemas/KeyInfo"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Five active keys already"
          }
        }
      }
    },
    "/v1/developer/keys/{id}": {
      "delete": {
        "tags": [
          "Keys"
        ],
        "summary": "Revoke a key",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such key"
          }
        }
      }
    },
    "/v1/media": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Upload a file to send",
        "description": "Up to 25 MB. The file is encrypted on DeepChat's server as it arrives and kept for 30 days. Send it afterwards with image or document in POST /v1/messages. Free plan: 100 MB stored per key, 100 uploads an hour. Send multipart/form-data with a \"file\" part, or the raw bytes with ?filename= and a Content-Type.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "filename": {
                    "type": "string"
                  }
                }
              }
            },
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "filename",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "For a raw body"
          }
        ],
        "responses": {
          "201": {
            "description": "Stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadedMedia"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "description": "Larger than 25 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many uploads",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "File sending is not enabled on this server",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "507": {
            "description": "The key has used its storage allowance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/media/{id}": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download a file (decrypted)",
        "description": "A file you uploaded, or one a person sent you (use media.id from the message.received event). For a file a person sent, DeepChat first fetches it from their phone, so the first request can take a few seconds, and answers 504 if the phone is offline: try again later. The answer is always an attachment and never runs in a browser.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such file, or it expired (files are kept for 30 days)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not get the file from the sender's phone",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The sender's phone is offline",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "507": {
            "description": "Storage allowance reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "deliveries": {
      "post": {
        "summary": "Events DeepChat sends to your webhook URL",
        "description": "Each delivery is signed. Header X-DeepChat-Signature is t=<unix seconds>,v1=<hex HMAC-SHA256 of t + \".\" + the raw body, keyed with your webhook secret>. Check it and reject timestamps older than 5 minutes. Answer 2xx; anything else is retried after 5 s, 30 s, 5 min, 30 min and 2 h, then dropped. Events for your account arrive strictly in order.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/EventReceived"
                  },
                  {
                    "$ref": "#/components/schemas/EventStatus"
                  },
                  {
                    "$ref": "#/components/schemas/EventButton"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Received"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "dc_live_... or dc_test_..."
      },
      "session": {
        "type": "http",
        "scheme": "bearer",
        "description": "The signed-in user's session token (the app)"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, revoked or unknown key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "The request is not valid",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "Me": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string"
          },
          "plan": {
            "type": "string"
          },
          "mode": {
            "enum": [
              "live",
              "test"
            ]
          },
          "hosting": {
            "enum": [
              "hosted",
              "sdk"
            ]
          },
          "limits": {
            "type": "object",
            "properties": {
              "per_second": {
                "type": "integer"
              },
              "per_month": {
                "type": "integer"
              }
            }
          },
          "used_this_month": {
            "type": "integer"
          }
        }
      },
      "SendRequest": {
        "type": "object",
        "required": [
          "to"
        ],
        "properties": {
          "to": {
            "type": "string",
            "example": "+919876543210"
          },
          "text": {
            "type": "string",
            "maxLength": 4000
          },
          "buttons": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 20
            },
            "description": "Needs text. Labels must differ."
          },
          "image": {
            "type": "object",
            "required": [
              "media_id"
            ],
            "properties": {
              "media_id": {
                "type": "string"
              },
              "caption": {
                "type": "string",
                "maxLength": 1000
              }
            }
          },
          "document": {
            "type": "object",
            "required": [
              "media_id"
            ],
            "properties": {
              "media_id": {
                "type": "string"
              },
              "filename": {
                "type": "string"
              },
              "caption": {
                "type": "string",
                "maxLength": 1000
              }
            }
          }
        }
      },
      "Sent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "const": "queued"
          },
          "sandbox": {
            "type": "boolean"
          }
        }
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "status": {
            "enum": [
              "queued",
              "sent",
              "delivered",
              "read",
              "failed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "error": {
            "type": "string"
          },
          "awaiting_acceptance": {
            "type": "boolean",
            "description": "Delivered, but the recipient has not accepted you yet"
          }
        }
      },
      "WebhookSettings": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "enum": [
                "message.received",
                "message.status",
                "button.clicked"
              ]
            }
          },
          "rotate_secret": {
            "type": "boolean"
          }
        }
      },
      "WebhookSaved": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "secret": {
            "type": "string",
            "description": "Only when created or rotated"
          }
        }
      },
      "KeyInfo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "prefix": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "mode": {
            "enum": [
              "live",
              "test"
            ]
          },
          "hosting": {
            "enum": [
              "hosted",
              "sdk"
            ]
          },
          "plan": {
            "type": "string"
          },
          "device_id": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EventReceived": {
        "type": "object",
        "properties": {
          "event": {
            "const": "message.received"
          },
          "id": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "type": {
            "enum": [
              "text",
              "media"
            ]
          },
          "text": {
            "type": "string"
          },
          "media": {
            "$ref": "#/components/schemas/MediaInfo"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EventStatus": {
        "type": "object",
        "properties": {
          "event": {
            "const": "message.status"
          },
          "id": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "status": {
            "enum": [
              "sent",
              "delivered",
              "read",
              "failed"
            ]
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UploadedMedia": {
        "type": "object",
        "properties": {
          "media_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "mime": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MediaInfo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "mime": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "caption": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "GET it with your key to download the file"
          }
        }
      },
      "EventButton": {
        "type": "object",
        "properties": {
          "event": {
            "const": "button.clicked"
          },
          "id": {
            "type": "string",
            "description": "The id of your message that had the buttons"
          },
          "from": {
            "type": "string"
          },
          "button": {
            "type": "string",
            "description": "b0, b1 or b2: the position of the button"
          },
          "title": {
            "type": "string",
            "description": "The label that was tapped"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
