Menu

Reference

Discovery

Discovery: 13 operations (discovery.balance, discovery.handles.resolve, discovery.icp_profiles.list, discovery.modes, discovery.quote, discovery.recipes.create, discovery.runs.list, discovery.runs.create, discovery.runs.get, discovery.runs.cancel, discovery.candidates.list, discovery.runs.preview_decision, discovery.candidates.promote).

MethodPathOperation
GET/v1/discovery/balancediscovery.balance
POST/v1/discovery/handles/resolvediscovery.handles.resolve
GET/v1/discovery/icp-profilesdiscovery.icp_profiles.list
GET/v1/discovery/modesdiscovery.modes
POST/v1/discovery/quotediscovery.quote
POST/v1/discovery/recipesdiscovery.recipes.create
GET/v1/discovery/runsdiscovery.runs.list
POST/v1/discovery/runsdiscovery.runs.create
GET/v1/discovery/runs/{run_id}discovery.runs.get
POST/v1/discovery/runs/{run_id}/canceldiscovery.runs.cancel
GET/v1/discovery/runs/{run_id}/candidatesdiscovery.candidates.list
POST/v1/discovery/runs/{run_id}/preview-decisiondiscovery.runs.preview_decision
POST/v1/discovery/runs/{run_id}/promotediscovery.candidates.promote

discovery.balance

GET/v1/discovery/balance

The organization's discovery token balance.

Tokens the organization can spend now (plan and purchased), tokens held by its runs that are still moving, and when the plan period ends. Shared by every workspace of the organization.

Operation iddiscovery.balance
Scopesdiscovery:read
Rate class`read`
IdempotencyNot applicable
KeysDeveloper keys

Response 200

FieldTypeRequiredDescription
available_tokensintegeryes-9007199254740991 to 9007199254740991
period_ends_atstring | nullyes
reserved_tokensintegeryes-9007199254740991 to 9007199254740991

Errors

StatusCodeWhen
404`DISCOVERY_CREDITS_404`The token ledger has no wallet for the organization.
404`DISCOVERY_ORGANIZATION_NOT_FOUND_404`The key's creator is no longer a member of the organization.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl "https://api.dmhero.ai/v1/discovery/balance" \
  -H "Authorization: Bearer $DMHERO_API_KEY"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "available_tokens": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "reserved_tokens": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "period_ends_at": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "available_tokens",
    "reserved_tokens",
    "period_ends_at"
  ],
  "additionalProperties": false
}

discovery.handles.resolve

POST/v1/discovery/handles/resolve

Resolve Instagram handles to accounts, to use as a recipe's seeds.

Up to ten handles per request. The answer does not say whether a handle was already known or looked up; a lookup may spend from the platform's vendor budget, which is why the scope is discovery:spend.

Operation iddiscovery.handles.resolve
Scopesdiscovery:spend
Rate class`discovery_spend`
Idempotency`Idempotency-Key` accepted
KeysDeveloper keys

Request body

FieldTypeRequiredDescription
handlesstring[]yes1 to 10 items

Response 200

FieldTypeRequiredDescription
resultsobject[]yes

Errors

StatusCodeWhen
404`DISCOVERY_ORGANIZATION_NOT_FOUND_404`The key's creator is no longer a member of the organization.
409`DISCOVERY_HANDLES_REPLAY_409`A repeated Idempotency-Key whose first answer could not be stored; send the request again with a new key.
503`DISCOVERY_HANDLES_PROVIDER_503`No account lookup is possible right now. Retry later.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/handles/resolve" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "handles": [
    "string"
  ]
}'
JSON Schema
Request body
{
  "type": "object",
  "properties": {
    "handles": {
      "minItems": 1,
      "maxItems": 10,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 200
      }
    }
  },
  "required": [
    "handles"
  ]
}
Response 200
{
  "type": "object",
  "properties": {
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "handle": {
            "type": "string"
          },
          "ig_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "found",
              "not_found",
              "private",
              "unavailable",
              "invalid"
            ]
          }
        },
        "required": [
          "handle",
          "ig_user_id",
          "status"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "results"
  ],
  "additionalProperties": false
}

discovery.icp_profiles.list

GET/v1/discovery/icp-profiles

The organization's customer profiles (ICP), each at its newest version.

Name a profile and version in a recipe's qualify step to judge the run's accounts against it.

Operation iddiscovery.icp_profiles.list
Scopesdiscovery:read
Rate class`read`
IdempotencyNot applicable
KeysDeveloper keys

Response 200

FieldTypeRequiredDescription
dataobject[]yes

Errors

StatusCodeWhen
404`DISCOVERY_ORGANIZATION_NOT_FOUND_404`The key's creator is no longer a member of the organization.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl "https://api.dmhero.ai/v1/discovery/icp-profiles" \
  -H "Authorization: Bearer $DMHERO_API_KEY"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "id",
          "name",
          "version"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "data"
  ],
  "additionalProperties": false
}

discovery.modes

GET/v1/discovery/modes

The discovery modes and what this deployment can run.

The mode catalog (rate per result, quote shape, availability) and the deployment's capabilities.

Operation iddiscovery.modes
Scopesdiscovery:read
Rate class`read`
IdempotencyNot applicable
KeysDeveloper keys

Response 200

FieldTypeRequiredDescription
capabilitiesobjectyes
modesobject[]yes

Errors

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl "https://api.dmhero.ai/v1/discovery/modes" \
  -H "Authorization: Bearer $DMHERO_API_KEY"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "modes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "family": {
            "type": "string"
          },
          "ratePerResult": {
            "type": "number"
          },
          "quoteShape": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "deployable": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "family",
          "ratePerResult",
          "quoteShape",
          "available",
          "deployable"
        ],
        "additionalProperties": {}
      }
    },
    "capabilities": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "modes",
    "capabilities"
  ],
  "additionalProperties": {}
}

discovery.quote

POST/v1/discovery/quote

Price a recipe in tokens without saving or starting anything.

Nothing is written, reserved or bought. Counts against the organization's daily budget of quotes (1 000 a day, shared with every response of POST /v1/discovery/runs that carries a quote).

Operation iddiscovery.quote
Scopesdiscovery:read
Rate class`discovery_quote`, plus dq_day
IdempotencyNot applicable
KeysDeveloper keys

Request body

FieldTypeRequiredDescription
docobjectyesA search recipe document ({ v: 1, steps: [...] }), as the app's recipe builder saves it.

Response 200

FieldTypeRequiredDescription
modestring | nullyes
quoteobjectyesThe price of a recipe in tokens (the app's Quote).
refusalobject | nullyes

Errors

StatusCodeWhen
400`DISCOVERY_RECIPE_400`The recipe document does not validate; error names the first problem.
404`DISCOVERY_CREDITS_404`The token ledger has no wallet for the organization.
404`DISCOVERY_ORGANIZATION_NOT_FOUND_404`The key's creator is no longer a member of the organization.
409`DISCOVERY_CEILING_409`The organization's spend ceiling could not be read; nothing was priced or reserved.
429`DISCOVERY_QUOTE_DAILY_LIMIT_429`The organization used its daily budget of quotes.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/quote" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "doc": {
    "v": 1,
    "steps": [
      {
        "type": "seed",
        "id": "s",
        "igUserIds": [
          "25025320"
        ]
      },
      {
        "type": "expand",
        "id": "e",
        "in": "s",
        "edge": "following",
        "limit": 100
      },
      {
        "type": "rank",
        "id": "r",
        "in": "e",
        "by": "score",
        "take": 50
      }
    ]
  }
}'
JSON Schema
Request body
{
  "type": "object",
  "properties": {
    "doc": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {},
      "description": "A search recipe document (`{ v: 1, steps: [...] }`), as the app's recipe builder saves it."
    }
  },
  "required": [
    "doc"
  ]
}
Response 200
{
  "type": "object",
  "properties": {
    "quote": {
      "type": "object",
      "properties": {
        "shape": {
          "type": "string",
          "enum": [
            "exact",
            "ceiling",
            "range"
          ]
        },
        "tokensMin": {
          "type": "number"
        },
        "tokensMax": {
          "type": "number"
        },
        "expectedResultsMin": {
          "type": [
            "number",
            "null"
          ]
        },
        "expectedResultsMax": {
          "type": [
            "number",
            "null"
          ]
        },
        "estimatedSeconds": {
          "type": "number"
        },
        "seedCount": {
          "type": "number"
        },
        "seedAudienceTotal": {
          "type": "number"
        },
        "lowYield": {
          "type": "boolean"
        },
        "refundThreshold": {
          "type": [
            "number",
            "null"
          ]
        },
        "blockedReason": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "spend_ceiling",
                "invalid_recipe"
              ]
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "shape",
        "tokensMin",
        "tokensMax",
        "expectedResultsMin",
        "expectedResultsMax",
        "estimatedSeconds",
        "seedCount",
        "seedAudienceTotal",
        "lowYield",
        "refundThreshold",
        "blockedReason"
      ],
      "additionalProperties": {},
      "description": "The price of a recipe in tokens (the app's Quote)."
    },
    "mode": {
      "type": [
        "string",
        "null"
      ]
    },
    "refusal": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "code": {
              "type": "string"
            },
            "message": {
              "type": "string"
            },
            "path": {
              "type": "string"
            }
          },
          "required": [
            "code",
            "message",
            "path"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "quote",
    "mode",
    "refusal"
  ],
  "additionalProperties": {}
}

discovery.recipes.create

POST/v1/discovery/recipes

Save a search recipe.

Recipes are saved once per organization per canonical document: saving the same search again answers 200 with the first save's id and name instead of 201.

Operation iddiscovery.recipes.create
Scopesdiscovery:spend
Rate class`write`
Idempotency`Idempotency-Key` accepted
KeysDeveloper keys

Request body

FieldTypeRequiredDescription
docobjectyesA search recipe document ({ v: 1, steps: [...] }), as the app's recipe builder saves it.
namestringyes

Response 201

FieldTypeRequiredDescription
hashstringyes
idstringyes
namestringyes

Errors

StatusCodeWhen
400`DISCOVERY_RECIPE_400`The recipe document does not validate; error names the first problem.
400`DISCOVERY_RECIPE_NAME_400`The recipe name is empty or longer than 200 characters.
404`DISCOVERY_ICP_PROFILE_404`The recipe's qualify step names a customer profile version that is missing, archived or another organization's.
404`DISCOVERY_ORGANIZATION_NOT_FOUND_404`The key's creator is no longer a member of the organization.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/recipes" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Fitness coaches PL",
  "doc": {
    "v": 1,
    "steps": [
      {
        "type": "seed",
        "id": "s",
        "igUserIds": [
          "25025320"
        ]
      },
      {
        "type": "expand",
        "id": "e",
        "in": "s",
        "edge": "following",
        "limit": 100
      },
      {
        "type": "rank",
        "id": "r",
        "in": "e",
        "by": "score",
        "take": 50
      }
    ]
  }
}'
JSON Schema
Request body
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "doc": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {},
      "description": "A search recipe document (`{ v: 1, steps: [...] }`), as the app's recipe builder saves it."
    }
  },
  "required": [
    "name",
    "doc"
  ]
}
Response 201
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "hash": {
      "type": "string"
    },
    "name": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "hash",
    "name"
  ],
  "additionalProperties": false
}

discovery.runs.list

GET/v1/discovery/runs

The workspace's runs, newest first.

Operation iddiscovery.runs.list
Scopesdiscovery:read
Rate class`read`
IdempotencyNot applicable
KeysDeveloper keys

Query parameters

NameTypeRequiredDescription
state"queued" | "running" | "awaiting_preview" | "partial" | "done" | "cancelled" | "failed"no
created_afterdate-timeno
limitintegerno1 to 50, default 20
cursorstringno1 to 2048 characters

Response 200

FieldTypeRequiredDescription
dataobject[]yes
has_morebooleanyes
next_cursorstring | nullyes

Errors

StatusCodeWhen
404`DISCOVERY_WORKSPACE_NOT_FOUND_404`The key's creator is no longer a member of the workspace's organization.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl "https://api.dmhero.ai/v1/discovery/runs" \
  -H "Authorization: Bearer $DMHERO_API_KEY"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "awaiting_preview",
              "partial",
              "done",
              "cancelled",
              "failed"
            ]
          },
          "progress": {
            "type": "object",
            "properties": {
              "done": {
                "type": "number"
              },
              "total": {
                "type": "number"
              },
              "step": {
                "type": "string"
              }
            },
            "required": [
              "done",
              "total",
              "step"
            ],
            "additionalProperties": false
          },
          "startedAt": {
            "type": "string"
          },
          "etaSeconds": {
            "type": [
              "number",
              "null"
            ]
          },
          "candidatesReady": {
            "type": "number"
          },
          "matchedTotal": {
            "type": "number"
          },
          "paidFor": {
            "type": "number"
          },
          "family": {
            "type": "string"
          },
          "previewReady": {
            "type": "boolean"
          },
          "previewDecision": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "pending",
                  "continue",
                  "stopped"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "skippedSources": {
            "type": "number"
          },
          "queuedBehind": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "runId": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  },
                  "etaAt": {
                    "type": "string"
                  }
                },
                "required": [
                  "runId",
                  "label",
                  "etaAt"
                ],
                "additionalProperties": {}
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "mode",
          "state",
          "progress",
          "startedAt",
          "etaSeconds",
          "candidatesReady",
          "matchedTotal",
          "paidFor",
          "family",
          "previewReady",
          "previewDecision",
          "skippedSources",
          "queuedBehind"
        ],
        "additionalProperties": {},
        "description": "One discovery run (the app's Run). The single-run read adds `activity` and, where the mode has one, `graph`, `likers` or `search`; the list adds `searchSummary`, `tokens` and `promotedCount`."
      }
    },
    "has_more": {
      "type": "boolean"
    },
    "next_cursor": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "data",
    "has_more",
    "next_cursor"
  ],
  "additionalProperties": false
}

discovery.runs.create

POST/v1/discovery/runs

Start a discovery run: price the recipe, reserve its tokens and queue it.

Send recipe_id (a saved recipe) or recipe ({ name, doc }, saved first, then started), not both. A refusal that is a price (402) carries the quote. Every request counts against the organization's daily budget of quotes, the one POST /v1/discovery/quote spends, and a spent budget answers 429 before anything is priced. Requires an Idempotency-Key: a retry with the same key never starts a second run.

Operation iddiscovery.runs.create
Scopesdiscovery:spend
Rate class`discovery_spend`, plus dq_day
Idempotency`Idempotency-Key` required
KeysDeveloper keys

Request body

FieldTypeRequiredDescription
recipeobjectno
recipe_iduuidno

Response 201

FieldTypeRequiredDescription
quoteobject | nullyesNull only on an idempotent replay, which rebuilds the run from its id.
reserved_tokensintegeryes-9007199254740991 to 9007199254740991
runobjectyesOne discovery run (the app's Run). The single-run read adds activity and, where the mode has one, graph, likers or search; the list adds searchSummary, tokens and promotedCount.

Errors

StatusCodeWhen
400`DISCOVERY_CREDITS_400`The token ledger refused the amount to reserve; nothing was reserved.
400`DISCOVERY_RECIPE_400`The recipe document does not validate; error names the first problem.
400`DISCOVERY_RECIPE_NAME_400`The recipe name is empty or longer than 200 characters.
402`DISCOVERY_CEILING_402`The organization reached its spend ceiling for the period; the body carries the quote.
402`DISCOVERY_CREDITS_402`The organization holds fewer tokens than the run needs; the body carries the quote.
404`DISCOVERY_CREDITS_404`The token ledger has no wallet for the organization.
404`DISCOVERY_ICP_PROFILE_404`The recipe's qualify step names a customer profile version that is missing, archived or another organization's.
404`DISCOVERY_RECIPE_404`The recipe does not exist in this organization.
404`DISCOVERY_WORKSPACE_NOT_FOUND_404`The key's creator is no longer a member of the workspace's organization.
409`DISCOVERY_CEILING_409`The spend ceiling could not be read, or the run would exceed its enrichment ratio; nothing was reserved.
409`DISCOVERY_QUALIFY_UNAVAILABLE_409`The recipe judges accounts against a customer profile, which is not available to this organization right now; the body carries the quote.
429`DISCOVERY_QUOTE_DAILY_LIMIT_429`The organization used its daily budget of quotes.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/runs" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "recipe": {
    "name": "Fitness coaches PL",
    "doc": {
      "v": 1,
      "steps": [
        {
          "type": "seed",
          "id": "s",
          "igUserIds": [
            "25025320"
          ]
        },
        {
          "type": "expand",
          "id": "e",
          "in": "s",
          "edge": "following",
          "limit": 100
        },
        {
          "type": "rank",
          "id": "r",
          "in": "e",
          "by": "score",
          "take": 50
        }
      ]
    }
  }
}'
JSON Schema
Request body
{
  "type": "object",
  "properties": {
    "recipe_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "recipe": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "doc": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {},
          "description": "A search recipe document (`{ v: 1, steps: [...] }`), as the app's recipe builder saves it."
        }
      },
      "required": [
        "name",
        "doc"
      ]
    }
  }
}
Response 201
{
  "type": "object",
  "properties": {
    "run": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "mode": {
          "type": "string"
        },
        "state": {
          "type": "string",
          "enum": [
            "queued",
            "running",
            "awaiting_preview",
            "partial",
            "done",
            "cancelled",
            "failed"
          ]
        },
        "progress": {
          "type": "object",
          "properties": {
            "done": {
              "type": "number"
            },
            "total": {
              "type": "number"
            },
            "step": {
              "type": "string"
            }
          },
          "required": [
            "done",
            "total",
            "step"
          ],
          "additionalProperties": false
        },
        "startedAt": {
          "type": "string"
        },
        "etaSeconds": {
          "type": [
            "number",
            "null"
          ]
        },
        "candidatesReady": {
          "type": "number"
        },
        "matchedTotal": {
          "type": "number"
        },
        "paidFor": {
          "type": "number"
        },
        "family": {
          "type": "string"
        },
        "previewReady": {
          "type": "boolean"
        },
        "previewDecision": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "pending",
                "continue",
                "stopped"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "skippedSources": {
          "type": "number"
        },
        "queuedBehind": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "runId": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "etaAt": {
                  "type": "string"
                }
              },
              "required": [
                "runId",
                "label",
                "etaAt"
              ],
              "additionalProperties": {}
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "mode",
        "state",
        "progress",
        "startedAt",
        "etaSeconds",
        "candidatesReady",
        "matchedTotal",
        "paidFor",
        "family",
        "previewReady",
        "previewDecision",
        "skippedSources",
        "queuedBehind"
      ],
      "additionalProperties": {},
      "description": "One discovery run (the app's Run). The single-run read adds `activity` and, where the mode has one, `graph`, `likers` or `search`; the list adds `searchSummary`, `tokens` and `promotedCount`."
    },
    "quote": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "shape": {
              "type": "string",
              "enum": [
                "exact",
                "ceiling",
                "range"
              ]
            },
            "tokensMin": {
              "type": "number"
            },
            "tokensMax": {
              "type": "number"
            },
            "expectedResultsMin": {
              "type": [
                "number",
                "null"
              ]
            },
            "expectedResultsMax": {
              "type": [
                "number",
                "null"
              ]
            },
            "estimatedSeconds": {
              "type": "number"
            },
            "seedCount": {
              "type": "number"
            },
            "seedAudienceTotal": {
              "type": "number"
            },
            "lowYield": {
              "type": "boolean"
            },
            "refundThreshold": {
              "type": [
                "number",
                "null"
              ]
            },
            "blockedReason": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "spend_ceiling",
                    "invalid_recipe"
                  ]
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "required": [
            "shape",
            "tokensMin",
            "tokensMax",
            "expectedResultsMin",
            "expectedResultsMax",
            "estimatedSeconds",
            "seedCount",
            "seedAudienceTotal",
            "lowYield",
            "refundThreshold",
            "blockedReason"
          ],
          "additionalProperties": {},
          "description": "The price of a recipe in tokens (the app's Quote)."
        },
        {
          "type": "null"
        }
      ],
      "description": "Null only on an idempotent replay, which rebuilds the run from its id."
    },
    "reserved_tokens": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "run",
    "quote",
    "reserved_tokens"
  ],
  "additionalProperties": false
}

discovery.runs.get

GET/v1/discovery/runs/{run_id}

One run, with its live activity. Poll every 10 seconds while it moves.

Operation iddiscovery.runs.get
Scopesdiscovery:read
Rate class`read`
IdempotencyNot applicable
KeysDeveloper keys

Path parameters

NameTypeRequiredDescription
run_idstringyesA run id (uuid). An id that is not a run of this workspace answers 404.. 1 to 64 characters

Response 200

FieldTypeRequiredDescription
candidatesReadynumberyes
etaSecondsnumber | nullyes
familystringyes
idstringyes
matchedTotalnumberyes
modestringyes
paidFornumberyes
previewDecision"pending" | "continue" | "stopped" | nullyes
previewReadybooleanyes
progressobjectyes
queuedBehindobject | nullyes
skippedSourcesnumberyes
startedAtstringyes
state"queued" | "running" | "awaiting_preview" | "partial" | "done" | "cancelled" | "failed"yes

Errors

StatusCodeWhen
404`DISCOVERY_RUN_NOT_FOUND_404`The run does not exist in this workspace (the same answer for another workspace's run).

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl "https://api.dmhero.ai/v1/discovery/runs/string" \
  -H "Authorization: Bearer $DMHERO_API_KEY"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "mode": {
      "type": "string"
    },
    "state": {
      "type": "string",
      "enum": [
        "queued",
        "running",
        "awaiting_preview",
        "partial",
        "done",
        "cancelled",
        "failed"
      ]
    },
    "progress": {
      "type": "object",
      "properties": {
        "done": {
          "type": "number"
        },
        "total": {
          "type": "number"
        },
        "step": {
          "type": "string"
        }
      },
      "required": [
        "done",
        "total",
        "step"
      ],
      "additionalProperties": false
    },
    "startedAt": {
      "type": "string"
    },
    "etaSeconds": {
      "type": [
        "number",
        "null"
      ]
    },
    "candidatesReady": {
      "type": "number"
    },
    "matchedTotal": {
      "type": "number"
    },
    "paidFor": {
      "type": "number"
    },
    "family": {
      "type": "string"
    },
    "previewReady": {
      "type": "boolean"
    },
    "previewDecision": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "pending",
            "continue",
            "stopped"
          ]
        },
        {
          "type": "null"
        }
      ]
    },
    "skippedSources": {
      "type": "number"
    },
    "queuedBehind": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "runId": {
              "type": "string"
            },
            "label": {
              "type": "string"
            },
            "etaAt": {
              "type": "string"
            }
          },
          "required": [
            "runId",
            "label",
            "etaAt"
          ],
          "additionalProperties": {}
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "mode",
    "state",
    "progress",
    "startedAt",
    "etaSeconds",
    "candidatesReady",
    "matchedTotal",
    "paidFor",
    "family",
    "previewReady",
    "previewDecision",
    "skippedSources",
    "queuedBehind"
  ],
  "additionalProperties": {},
  "description": "One discovery run (the app's Run). The single-run read adds `activity` and, where the mode has one, `graph`, `likers` or `search`; the list adds `searchSummary`, `tokens` and `promotedCount`."
}

discovery.runs.cancel

POST/v1/discovery/runs/{run_id}/cancel

Cancel a run that is still moving and release its unspent tokens.

A run that has already ended answers cancelled: false with its current state.

Operation iddiscovery.runs.cancel
Scopesdiscovery:spend
Rate class`write`
Idempotency`Idempotency-Key` accepted
KeysDeveloper keys
DestructiveYes: it removes or stops something that cannot be restored by repeating the call

Path parameters

NameTypeRequiredDescription
run_idstringyesA run id (uuid). An id that is not a run of this workspace answers 404.. 1 to 64 characters

Response 200

FieldTypeRequiredDescription
cancelledbooleanyes
runobjectyesOne discovery run (the app's Run). The single-run read adds activity and, where the mode has one, graph, likers or search; the list adds searchSummary, tokens and promotedCount.

Errors

StatusCodeWhen
404`DISCOVERY_RUN_NOT_FOUND_404`The run does not exist in this workspace (the same answer for another workspace's run).

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/runs/string/cancel" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "cancelled": {
      "type": "boolean"
    },
    "run": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "mode": {
          "type": "string"
        },
        "state": {
          "type": "string",
          "enum": [
            "queued",
            "running",
            "awaiting_preview",
            "partial",
            "done",
            "cancelled",
            "failed"
          ]
        },
        "progress": {
          "type": "object",
          "properties": {
            "done": {
              "type": "number"
            },
            "total": {
              "type": "number"
            },
            "step": {
              "type": "string"
            }
          },
          "required": [
            "done",
            "total",
            "step"
          ],
          "additionalProperties": false
        },
        "startedAt": {
          "type": "string"
        },
        "etaSeconds": {
          "type": [
            "number",
            "null"
          ]
        },
        "candidatesReady": {
          "type": "number"
        },
        "matchedTotal": {
          "type": "number"
        },
        "paidFor": {
          "type": "number"
        },
        "family": {
          "type": "string"
        },
        "previewReady": {
          "type": "boolean"
        },
        "previewDecision": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "pending",
                "continue",
                "stopped"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "skippedSources": {
          "type": "number"
        },
        "queuedBehind": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "runId": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "etaAt": {
                  "type": "string"
                }
              },
              "required": [
                "runId",
                "label",
                "etaAt"
              ],
              "additionalProperties": {}
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "mode",
        "state",
        "progress",
        "startedAt",
        "etaSeconds",
        "candidatesReady",
        "matchedTotal",
        "paidFor",
        "family",
        "previewReady",
        "previewDecision",
        "skippedSources",
        "queuedBehind"
      ],
      "additionalProperties": {},
      "description": "One discovery run (the app's Run). The single-run read adds `activity` and, where the mode has one, `graph`, `likers` or `search`; the list adds `searchSummary`, `tokens` and `promotedCount`."
    }
  },
  "required": [
    "cancelled",
    "run"
  ],
  "additionalProperties": false
}

discovery.candidates.list

GET/v1/discovery/runs/{run_id}/candidates

A run's results, in its delivery order.

Operation iddiscovery.candidates.list
Scopesdiscovery:read
Rate class`read`
IdempotencyNot applicable
KeysDeveloper keys

Path parameters

NameTypeRequiredDescription
run_idstringyesA run id (uuid). An id that is not a run of this workspace answers 404.. 1 to 64 characters

Query parameters

NameTypeRequiredDescription
limitintegerno1 to 100, default 50
cursorstringno1 to 2048 characters

Response 200

FieldTypeRequiredDescription
dataobject[]yes
has_morebooleanyes
next_cursorstring | nullyes

Errors

StatusCodeWhen
404`DISCOVERY_RUN_NOT_FOUND_404`The run does not exist in this workspace (the same answer for another workspace's run).

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl "https://api.dmhero.ai/v1/discovery/runs/string/candidates" \
  -H "Authorization: Bearer $DMHERO_API_KEY"
JSON Schema
Response 200
{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "fullName": {
            "type": [
              "string",
              "null"
            ]
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "followers": {
            "type": [
              "number",
              "null"
            ]
          },
          "following": {
            "type": [
              "number",
              "null"
            ]
          },
          "postsCount": {
            "type": [
              "number",
              "null"
            ]
          },
          "bioExcerpt": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "lang": {
            "type": [
              "string",
              "null"
            ]
          },
          "websiteDomain": {
            "type": [
              "string",
              "null"
            ]
          },
          "seedHits": {
            "type": "number"
          },
          "pathCount": {
            "type": "number"
          },
          "paths": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "seedHandle": {
                  "type": "string"
                },
                "viaHandle": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "edge": {
                  "type": "string"
                }
              },
              "required": [
                "seedHandle",
                "viaHandle",
                "edge"
              ],
              "additionalProperties": {}
            }
          },
          "state": {
            "type": "string",
            "enum": [
              "new",
              "shortlisted",
              "rejected",
              "promoted"
            ]
          },
          "duplicateOf": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "leadId": {
                    "type": "string"
                  },
                  "since": {
                    "type": "string"
                  }
                },
                "required": [
                  "leadId",
                  "since"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "handle",
          "fullName",
          "avatarUrl",
          "followers",
          "following",
          "postsCount",
          "bioExcerpt",
          "category",
          "lang",
          "websiteDomain",
          "seedHits",
          "pathCount",
          "paths",
          "state",
          "duplicateOf"
        ],
        "additionalProperties": {},
        "description": "One result of a run (the app's Candidate), in the run's delivery order. A run that judges against a customer profile adds `verdict`; a local business run adds `local`."
      }
    },
    "has_more": {
      "type": "boolean"
    },
    "next_cursor": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "data",
    "has_more",
    "next_cursor"
  ],
  "additionalProperties": false
}

discovery.runs.preview_decision

POST/v1/discovery/runs/{run_id}/preview-decision

Answer a run's preview: continue spending its reservation, or stop and release it.

Operation iddiscovery.runs.preview_decision
Scopesdiscovery:spend
Rate class`write`
Idempotency`Idempotency-Key` accepted
KeysDeveloper keys

Path parameters

NameTypeRequiredDescription
run_idstringyesA run id (uuid). An id that is not a run of this workspace answers 404.. 1 to 64 characters

Request body

FieldTypeRequiredDescription
decision"continue" | "stop"yes

Response 200

FieldTypeRequiredDescription
candidatesReadynumberyes
etaSecondsnumber | nullyes
familystringyes
idstringyes
matchedTotalnumberyes
modestringyes
paidFornumberyes
previewDecision"pending" | "continue" | "stopped" | nullyes
previewReadybooleanyes
progressobjectyes
queuedBehindobject | nullyes
skippedSourcesnumberyes
startedAtstringyes
state"queued" | "running" | "awaiting_preview" | "partial" | "done" | "cancelled" | "failed"yes

Errors

StatusCodeWhen
404`DISCOVERY_RUN_NOT_FOUND_404`The run does not exist in this workspace (the same answer for another workspace's run).
409`DISCOVERY_PREVIEW_NOT_PENDING_409`The run is not waiting for a preview decision (it was answered, or it moved on).

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/runs/string/preview-decision" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "decision": "continue"
}'
JSON Schema
Request body
{
  "type": "object",
  "properties": {
    "decision": {
      "type": "string",
      "enum": [
        "continue",
        "stop"
      ]
    }
  },
  "required": [
    "decision"
  ]
}
Response 200
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "mode": {
      "type": "string"
    },
    "state": {
      "type": "string",
      "enum": [
        "queued",
        "running",
        "awaiting_preview",
        "partial",
        "done",
        "cancelled",
        "failed"
      ]
    },
    "progress": {
      "type": "object",
      "properties": {
        "done": {
          "type": "number"
        },
        "total": {
          "type": "number"
        },
        "step": {
          "type": "string"
        }
      },
      "required": [
        "done",
        "total",
        "step"
      ],
      "additionalProperties": false
    },
    "startedAt": {
      "type": "string"
    },
    "etaSeconds": {
      "type": [
        "number",
        "null"
      ]
    },
    "candidatesReady": {
      "type": "number"
    },
    "matchedTotal": {
      "type": "number"
    },
    "paidFor": {
      "type": "number"
    },
    "family": {
      "type": "string"
    },
    "previewReady": {
      "type": "boolean"
    },
    "previewDecision": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "pending",
            "continue",
            "stopped"
          ]
        },
        {
          "type": "null"
        }
      ]
    },
    "skippedSources": {
      "type": "number"
    },
    "queuedBehind": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "runId": {
              "type": "string"
            },
            "label": {
              "type": "string"
            },
            "etaAt": {
              "type": "string"
            }
          },
          "required": [
            "runId",
            "label",
            "etaAt"
          ],
          "additionalProperties": {}
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "mode",
    "state",
    "progress",
    "startedAt",
    "etaSeconds",
    "candidatesReady",
    "matchedTotal",
    "paidFor",
    "family",
    "previewReady",
    "previewDecision",
    "skippedSources",
    "queuedBehind"
  ],
  "additionalProperties": {},
  "description": "One discovery run (the app's Run). The single-run read adds `activity` and, where the mode has one, `graph`, `likers` or `search`; the list adds `searchSummary`, `tokens` and `promotedCount`."
}

discovery.candidates.promote

POST/v1/discovery/runs/{run_id}/promote

Move a run's results into the CRM as leads.

Send candidate_ids (1 to 200) or all: true (the next 200 results not yet moved, not rejected and with a usable Instagram handle, in delivery order; has_more says whether to send it again). Someone the workspace already has becomes a duplicate of that lead, never a second lead. folder_id and tags apply to the leads this request creates, in a step after the move: if that step fails the leads exist unfiled, and moving the same results again creates nothing and files nothing, so file them through the leads operations. Moving the same results twice creates nothing the second time.

Operation iddiscovery.candidates.promote
Scopesdiscovery:read, leads:write
Rate class`discovery_spend`, plus leads_created_day
Idempotency`Idempotency-Key` accepted
KeysDeveloper keys

Path parameters

NameTypeRequiredDescription
run_idstringyesA run id (uuid). An id that is not a run of this workspace answers 404.. 1 to 64 characters

Request body

FieldTypeRequiredDescription
alltrueno
candidate_idsuuid[]no1 to 200 items
folder_iduuidno
tagsstring[]no1 to 20 items

Response 200

FieldTypeRequiredDescription
createdintegeryes-9007199254740991 to 9007199254740991
duplicatesintegeryes-9007199254740991 to 9007199254740991
has_morebooleanyes
lead_idsstring[]yesThe leads this request created. Empty on an idempotent replay.
run_idstringyes

Errors

StatusCodeWhen
404`DISCOVERY_CANDIDATES_NOT_FOUND_404`Some of the candidate ids are not results of this run; nothing was moved.
404`DISCOVERY_RUN_NOT_FOUND_404`The run does not exist in this workspace (the same answer for another workspace's run).
404`FOLDER_NOT_FOUND_404`The folder does not exist in this workspace; nothing was moved.
409`DISCOVERY_CANDIDATE_NOT_PROMOTABLE_409`One of the results has no usable Instagram handle; nothing was moved.
409`DISCOVERY_RUN_STALE_409`The run finished too long ago for its results to be trusted; run the search again.
429`LEADS_DAILY_LIMIT_429`The organization reached its daily limit of leads created through the API.

Every operation can also answer the shared errors: authentication, scopes, rate limits and validation.

Example

bash
curl -X POST "https://api.dmhero.ai/v1/discovery/runs/string/promote" \
  -H "Authorization: Bearer $DMHERO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "all": true,
  "tags": [
    "discovery"
  ]
}'
JSON Schema
Request body
{
  "type": "object",
  "properties": {
    "candidate_ids": {
      "minItems": 1,
      "maxItems": 200,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
      }
    },
    "all": {
      "type": "boolean",
      "const": true
    },
    "folder_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "tags": {
      "minItems": 1,
      "maxItems": 20,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 50
      }
    }
  }
}
Response 200
{
  "type": "object",
  "properties": {
    "run_id": {
      "type": "string"
    },
    "created": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "duplicates": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "lead_ids": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "The leads this request created. Empty on an idempotent replay."
    },
    "has_more": {
      "type": "boolean"
    }
  },
  "required": [
    "run_id",
    "created",
    "duplicates",
    "lead_ids",
    "has_more"
  ],
  "additionalProperties": false
}
Discovery | DM Hero Developers