Doppel API

Doppel V1 REST API — create and triage Brand Protection alerts on suspicious URLs and phone numbers, scan URLs for suspiciousness, submit and manage reports, manage brands and protected assets, and read Human Risk Management phishing-simulation campaigns and events. Authenticated with x-api-key + x-user-api-key headers. Six webhooks deliver real-time events.

OpenAPI Specification

doppel-openapi-original.yml Raw ↑
openapi: 3.1.0
info:
  version: 1.0.0
  title: Doppel
  description: APIs to interface with Doppel's core system for social engineering defense — Brand Protection
    alerts, URL scanning, reports, protected assets, and Human Risk Management campaigns.
  contact:
    name: Doppel API Support
    url: https://doppel.readme.io/
servers:
- url: https://api.doppel.com/v1
x-readme:
  parameter-ordering:
  - path
  - header
  - query
  - body
  - cookie
  - form
paths:
  /alert:
    post:
      description: 'Creates an alert for a specific entity (URL or phone number) within Doppel''s Brand
        Protection product.


        **Behavior:**

        - If an alert already exists for the entity, the existing alert is returned (HTTP 200).

        - If the alert exists but with a different brand than requested, returns HTTP 409.

        - After creation, the alert is automatically processed through Doppel''s triage workflow.

        - If `source` is provided, it must exactly match the "API value" field of an external source configured
        in your organization''s settings in Doppel Vision.


        **Returns HTTP 400 when:**

        - `entity` is missing, not a valid URL, or not a valid phone number.

        - `entity` is a protected asset (e.g. google.com).

        - `tags` is not a list of non-empty strings.

        - `source` does not match any configured external source for the organization.

        - `files` contains more than 10 items, duplicate file names, or is missing `file_to_upload`.

        '
      summary: Create Alert
      operationId: create_alert
      tags:
      - alerts
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 180
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: Content-Type
        in: header
        required: true
        description: ''
        schema:
          type: string
          enum:
          - application/json
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
              - entity
              properties:
                entity:
                  type: string
                  description: URL or phone number to be alerted.
                  example: https://suspicious-site.example.com
                brand:
                  type: string
                  description: The brand name to associate with the alert. Must match an active (non-archived)
                    brand in your organization. Case-insensitive.
                  example: Acme Corp
                tags:
                  type: array
                  items:
                    type: string
                  description: 'Array of tag names to associate with the alert. Tags must already exist
                    for your organization or be global tags.

                    Tags that don''t exist will be silently ignored.

                    '
                  example:
                  - Credential Theft
                  - NSFW
                  - Malicious
                  - Spam
                source:
                  type: string
                  description: Custom API source identifier configured in organization settings in Doppel
                    Vision. Must exactly match the "API value" field of the desired source.
                  example: slack-integration
                files:
                  type: array
                  maxItems: 10
                  description: 'Optional list of files to upload with the alert. Each file must include
                    base64-encoded content. Maximum 10 files per request.

                    For Telco alerts, uploaded image files may also be used as the alert''s canonical
                    screenshot. If an uploaded Telco image is selected as the screenshot, Doppel stores
                    a separate screenshot copy for alert review and downstream enforcement workflows while
                    preserving the original Uploaded Files copy. Only upload image files that are appropriate
                    to display as alert evidence.'
                  items:
                    $ref: '#/components/schemas/FileInput'
        required: true
      responses:
        '200':
          description: Alert already exists
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertCreatedResponse'
        '201':
          description: Submitted report successfully
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertCreatedResponse'
        '400':
          description: Invalid input
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Brand not found or archived for this organization
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Alert already exists with a different brand
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertCreatedResponse'
        '429':
          description: Rate limit exceeded
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
      x-google-quota:
        metricCosts:
          create-alert-request: 1
    get:
      description: 'Retrieves the details of a single alert by its ID or entity value.


        **You must provide exactly one of `id` or `entity`**. Providing neither or both returns HTTP 400.


        Returns the full alert object including audit logs, tags, entity content, and a signed screenshot
        URL (expires in 1 hour).

        '
      summary: Get Alert
      operationId: get-alert
      tags:
      - alerts
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: id
        in: query
        required: false
        description: The alert ID (format ORG-1234). Mutually exclusive with `entity`.
        example: ACM-1234
        schema:
          type: string
      - name: entity
        in: query
        required: false
        description: The URL, phone number, or email of the alert to retrieve. Mutually exclusive with
          `id`.
        example: https://suspicious-site.example.com
        schema:
          type: string
      responses:
        '200':
          description: Alert successfully retrieved
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertResponse'
        '400':
          description: Invalid input
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Alert not found
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
      x-google-quota:
        metricCosts:
          get-alert-request: 1
    put:
      description: 'Updates an alert''s information in Doppel''s system. Works across all modules.


        **You must provide exactly one of `id` or `entity`** to identify the alert. At least one field
        to update must be provided in the request body.


        **Paired parameters:**

        - `tag_action` and `tag_name` must both be provided together.

        - `file_action` and `files` must both be provided together.

        '
      summary: Update Alert
      operationId: update_alert
      tags:
      - alerts
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: Content-Type
        in: header
        required: true
        description: ''
        schema:
          type: string
          enum:
          - application/json
      - name: id
        in: query
        required: false
        description: The alert ID (format ORG-1234). Mutually exclusive with `entity`.
        example: ACM-1234
        schema:
          type: string
      - name: entity
        in: query
        required: false
        description: The URL, phone number, or email of the alert to update. Mutually exclusive with `id`.
        example: https://suspicious-site.example.com
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                queue_state:
                  description: 'New queue state to update the alert with.

                    (Note: taken_down is equivalent to Resolved in the Doppel Vision App)

                    '
                  type: string
                  enum:
                  - doppel_review
                  - needs_confirmation
                  - actioned
                  - taken_down
                  - monitoring
                  - archived
                  example: actioned
                entity_state:
                  $ref: '#/components/schemas/EntityState'
                comment:
                  type: string
                  description: A new comment to add to the alert.
                  example: Confirmed phishing — proceeding with takedown.
                tag_action:
                  $ref: '#/components/schemas/TagActionType'
                tag_name:
                  type: string
                  description: Name of the tag to add or remove. Must be provided together with `tag_action`.
                  example: Credential Theft
                file_action:
                  $ref: '#/components/schemas/FileActionType'
                files:
                  type: array
                  maxItems: 10
                  description: 'List of files to upload or delete. Must be provided together with `file_action`.
                    Maximum 10 files per request. When file_action is "upload", each file must include
                    base64-encoded content in file_to_upload. When file_action is "delete", only file_name
                    is required.

                    For Telco alerts, uploaded image files may also be used as the alert''s canonical
                    screenshot. If an uploaded Telco image is selected as the screenshot, Doppel stores
                    a separate screenshot copy for alert review and downstream enforcement workflows while
                    preserving the original Uploaded Files copy. Deleting the uploaded-file copy does
                    not delete the canonical screenshot copy.'
                  items:
                    $ref: '#/components/schemas/FileInput'
        description: 'Fields to update on the alert. At least one field is required.

          `tag_action`/`tag_name` and `file_action`/`files` are paired — both in each pair must be provided
          together.

          '
      responses:
        '200':
          description: Updated alert successfully
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertResponse'
        '400':
          description: Invalid input
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Alert or tag not found
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
      x-google-quota:
        metricCosts:
          update-alert-request: 1
  /alert/referrer:
    post:
      description: This endpoint submits referrer logs to Doppel for processing. These logs capture URLs
        that direct visitors to a site and may help identify threats or suspicious activity. Submitting
        a referrer log does not guarantee that an alert will be generated. Instead, the submitted URL
        will be analyzed as part of Doppel's referrer log processing.
      summary: Submit Referrer Logs
      operationId: submit-referrer-logs
      tags:
      - alerts
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30
      security:
      - api_key: []
        user_api_key: []
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: Content-Type
        in: header
        required: true
        description: ''
        schema:
          type: string
          enum:
          - application/json
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
              - referrer_url
              properties:
                referrer_url:
                  type: string
                  description: The referrer URL where the user came from (required). Must not be a known
                    protected asset for your organization.
                  example: https://suspicious-referrer.example.com/phish
                destination_url:
                  type: string
                  description: The URL where the user navigated to (optional). If provided and not already
                    a known protected asset, it will be automatically added as a protected asset for your
                    organization.
                  example: https://your-company.com/login
        description: Referrer log information to be submitted
        required: true
      x-google-quota:
        metricCosts:
          submit-referrer-logs-request: 1
      responses:
        '202':
          description: Request accepted for processing - URL has been submitted for potential alert creation
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Successful referrer log submission.
        '400':
          description: Bad request - missing required field, invalid URL format, or URL is a known internal
            asset
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized - invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /alerts:
    get:
      description: 'Returns a paginated list of alerts across all modules, with support for filtering
        and sorting.


        **Sorting:** If `sort_type` is provided, `sort_order` must also be provided. If neither is provided,
        defaults to `date_sourced` descending.


        **Pagination:** Zero-indexed. Default page_size is 30, maximum is 200.


        **Note:** In this endpoint''s response, the alert timestamp field is named `last_activity_timestamp`
        (instead of `last_activity` as returned by GET /alert, PUT /alert, and POST /alert).

        '
      summary: Get Alerts
      operationId: get-alerts
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60
      tags:
      - alerts
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: search_key
        in: query
        description: Search alerts by URL. Currently only URL search is supported.
        required: false
        example: example.com
        schema:
          type: string
      - $ref: '#/components/parameters/QueueStateFilter'
      - $ref: '#/components/parameters/Product'
      - name: created_before
        in: query
        description: Filter alerts created before this date (ISO 8601 format).
        required: false
        example: '2024-01-05T13:45:30'
        schema:
          type: string
          format: date-time
      - name: created_after
        in: query
        description: Filter alerts created after this date (ISO 8601 format).
        required: false
        example: '2024-01-01T00:00:00'
        schema:
          type: string
          format: date-time
      - name: last_activity_timestamp
        in: query
        description: Filter alerts with last activity at or after this date (ISO 8601 format).
        required: false
        example: '2024-01-05T13:45:30'
        schema:
          type: string
          format: date-time
      - $ref: '#/components/parameters/SortType'
      - $ref: '#/components/parameters/SortOrder'
      - name: page
        in: query
        description: Page number for pagination (zero-indexed). Defaults to 0.
        required: false
        schema:
          type: integer
          default: 0
      - name: page_size
        in: query
        description: Number of alerts per page (default 30, max 200)
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 200
          default: 30
      - name: tags
        in: query
        description: Comma-separated list of tag names to filter alerts by.
        required: false
        example: Credential Theft,Malicious
        style: form
        explode: false
        schema:
          type: array
          items:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  alerts:
                    type: array
                    items:
                      $ref: '#/components/schemas/AlertResponse'
                  metadata:
                    type: object
                    properties:
                      count:
                        type: integer
                        description: Total count of alerts
                      page:
                        type: integer
                        description: Current page number
                      total_pages:
                        type: integer
                        description: Total number of pages
                      page_size:
                        type: integer
                        description: Number of alerts per page
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
      x-google-quota:
        metricCosts:
          get-alerts-request: 1
  /brands:
    get:
      description: List active brands for the authenticated organization. Filter by brand_type (organization_brand
        or personal_brand) and/or name, with pagination support. Only returns active brands (archived
        brands are excluded).
      summary: List Brands
      operationId: list-brands
      tags:
      - brands
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: brand_type
        in: query
        description: Filter by brand type (organization_brand or personal_brand)
        required: false
        schema:
          type: string
          enum:
          - organization_brand
          - personal_brand
      - name: name
        in: query
        description: Search by brand name (case-insensitive contains)
        required: false
        schema:
          type: string
      - name: limit
        in: query
        description: Maximum number of results to return
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 1000
          default: 100
      - name: offset
        in: query
        description: Number of results to skip for pagination
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Successfully retrieved brands
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetBrandResponse'
        '400':
          description: Invalid request parameters
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Invalid query parameter (e.g. out-of-range limit/offset, invalid brand_type)
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
  /brands/{brand_id}:
    delete:
      description: Delete (archive) a brand by ID. This soft-deletes the brand by setting its status to
        archived, along with disabling related sourcing queries, trademarks, customer assets, and designated
        takedown assets.
      summary: Delete Brand
      operationId: delete-brand
      tags:
      - brands
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: brand_id
        in: path
        description: UUID of the brand to delete
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Brand successfully deleted
          headers: {}
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: UUID of the deleted brand
        '401':
          description: Unauthorized
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden - insufficient permissions
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Brand not found
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Invalid request - malformed UUID in path
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
  /hrm/campaigns:
    get:
      description: List phishing simulation campaigns for the authenticated organization. Supports filtering
        by status, campaign type, date range, and test flag. Returns paginated results.
      summary: List Campaigns
      operationId: list-hrm-campaigns
      tags:
      - hrm
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: status
        in: query
        description: Filter by campaign status (e.g. pending, in_progress, finished)
        schema:
          type: string
      - name: campaign_type
        in: query
        description: Filter by campaign type (e.g. simulation)
        schema:
          type: string
      - name: launch_date_after
        in: query
        description: Filter campaigns launched after this ISO 8601 date
        schema:
          type: string
      - name: launch_date_before
        in: query
        description: Filter campaigns launched before this ISO 8601 date
        schema:
          type: string
      - name: is_test
        in: query
        description: Include test campaigns (defaults to false)
        schema:
          type: boolean
          default: false
      - name: sort_by
        in: query
        description: 'Sort field: launch_date, created_at, or name'
        schema:
          type: string
          default: created_at
      - name: sort_order
        in: query
        description: 'Sort order: asc or desc'
        schema:
          type: string
          default: desc
      - name: limit
        in: query
        description: Maximum number of results to return
        schema:
          type: integer
          minimum: 1
          maximum: 1000
          default: 100
      - name: offset
        in: query
        description: Number of results to skip for pagination
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: List of campaigns
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListCampaignsResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_key: []
        user_api_key: []
  /hrm/campaigns/{campaign_id}:
    get:
      description: Get a single phishing simulation campaign by ID for the authenticated organization.
      summary: Get Campaign
      operationId: get-hrm-campaign
      tags:
      - hrm
      x-google-backend:
        address: https://external-api-gateway-725058953679.us-west1.run.app
        jwt_audience: https://external-api-gateway-725058953679.us-west1.run.app
        path_translation: APPEND_PATH_TO_ADDRESS
      parameters:
      - $ref: '#/components/parameters/XOrganizationCodeHeader'
      - name: campaign_id
        in: path
        required: true
        description: Campaign ID
        schema:
          type: string
      responses:
        '200':
          description: Campaign details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HrmCampaignResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Campaign not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - api_

# --- truncated at 32 KB (111 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/doppel/refs/heads/main/openapi/doppel-openapi-original.yml