> ## Documentation Index
> Fetch the complete documentation index at: https://docs.seamless.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a deterministic agent workflow with Seamless

> Build a repeatable AI agent workflow that covers authentication, search, research, and result retrieval with clear branching rules and stop conditions.

A deterministic agent workflow removes ambiguity from every step: the agent always knows which endpoint to call next, which identifier to carry forward, and when to stop. This page defines the full sequence, the state the agent must maintain, branching rules for result delivery, stop conditions, and error handling — so you can build an agent that completes the workflow reliably without repeating steps or losing identifiers.

## Prerequisites

<Note>
  Before building an agent workflow, read [Authenticate and make your first request](/authenticate-and-make-your-first-request) to understand how authentication works and make a successful API call. Related guides: [Choose the right workflow](/choose-the-right-workflow) and [Understand identifiers and request flow](/understand-identifiers-and-request-flow).
</Note>

## Workflow sequence

<Steps>
  <Step title="Choose an authentication method">
    Select OAuth or API key. For OAuth, complete [OAuth](/authentication/oauth) and store the access token for `Authorization: Bearer`. For an API key, create one under [Settings → Public API](/authentication/api-keys), store it for the `Token` header, and skip the token request step.
  </Step>

  <Step title="Decide whether the task starts with companies or contacts">
    Determine from the task input whether the agent should call the company or contact endpoint family. This decision determines every endpoint the agent calls for the rest of the workflow.
  </Step>

  <Step title="Run the matching search step">
    Call [Search companies](/searchcompanies) or [Search contacts](/searchcontacts) depending on the decision in the previous step. Apply filters to narrow results.
  </Step>

  <Step title="Save the search result identifier">
    Review the response and identify the correct matching record. Save its `searchResultId` before proceeding. Do not advance to the next step until this identifier is stored.
  </Step>

  <Step title="Start the matching research request">
    Pass the saved `searchResultId` to [Research companies](/researchcompanies) or [Research contacts](/researchcontacts). The API returns a `requestId`.
  </Step>

  <Step title="Save the request identifier">
    Save the `requestId` returned by the research endpoint. Do not overwrite the `searchResultId` — keep both identifiers stored separately until the workflow completes.
  </Step>

  <Step title="Choose one result delivery method">
    Select one delivery method for this request: [webhooks](/receive-research-results-with-webhooks), [Poll company research results](/pollcompanyresearchresults), or [Poll contact research results](/pollcontactsresearchresults). Use the saved `requestId` for polling or to match against incoming webhook payloads.
  </Step>

  <Step title="Continue only after receiving the completed result">
    Wait until the agent has the full enriched record before advancing to downstream application logic. Do not proceed on a `researching` status or partial response.
  </Step>
</Steps>

<Warning>
  Never hard-code API keys in source code. Store API keys and access tokens in environment variables or a secrets manager. Exposing credentials in code risks unauthorized API access and credit consumption.
</Warning>

## Recommended agent state

The agent must persist these values across steps. Losing any of them requires restarting from an earlier step.

| Value                                  | When to store it     | Why it matters                                                     |
| -------------------------------------- | -------------------- | ------------------------------------------------------------------ |
| Access token or API key                | After authentication | Required as a header on every subsequent API request               |
| `searchResultId`                       | After search         | Passed to the research endpoint to identify which record to enrich |
| `requestId`                            | After research       | Passed to the polling endpoint or matched against webhook payloads |
| Workflow type (`company` or `contact`) | Before research      | Determines which endpoint family to call at every step             |

## Branching rules

<Note>
  **When to start with search**: always start with search when the agent needs to find targets. Start with research only when the agent already has a valid `searchResultId` from a previous step.

  **How to choose a result delivery method**: use webhooks when your application can receive incoming `POST` requests at a public HTTPS endpoint. Use polling when you want to check status on demand without a public endpoint. Use both only when your application explicitly requires both delivery paths for the same request.
</Note>

## Stop conditions

The agent must stop and not retry when any of the following occurs:

* The agent received the completed research result and downstream logic has what it needs.
* The agent cannot determine which endpoint family to call next.
* The agent does not have the identifier required for the next step.
* The research request returned an `error` poll status (or a non-terminal status you cannot resolve).

When the agent stops because of a missing identifier or unclear next step, return the missing input or identifier to the caller instead of repeating the previous request. When the agent stops because research failed, return the `requestId` and error status to the caller. Do not retry automatically.

## Error handling

1. **Authentication fails** — stop and request a valid access token or API key before calling any search or research endpoint.
2. **Search returns no usable result** — stop and return the missing target information to the caller. Do not start a research request without a confirmed `searchResultId`.
3. **Research poll returns `error` status** — return the `requestId` and status to the caller. Surface this to the caller rather than retrying automatically.
4. **Polling does not return a completed result** — keep the saved `requestId`, wait before polling again, and continue polling until the request reaches a terminal status.
5. **Webhook payload cannot be matched to a saved `requestId`** — ignore the payload or route it for manual review. Do not process unmatched payloads.

## Recommended workflows

<Tabs>
  <Tab title="Company">
    <Steps>
      <Step title="Authenticate">
        Use [OAuth](/authentication/oauth) (`Authorization: Bearer`) or an [API key](/authentication/api-keys) (`Token`). Store the credential.
      </Step>

      <Step title="Search companies">
        Call [Search companies](/searchcompanies) with the relevant filters.
      </Step>

      <Step title="Save the search result identifier">
        Identify the matching record and save its `searchResultId`.
      </Step>

      <Step title="Research companies">
        Pass the `searchResultId` to [Research companies](/researchcompanies). The API returns a `requestId`.
      </Step>

      <Step title="Save the request identifier">
        Save the `requestId`. Keep it separate from the `searchResultId`.
      </Step>

      <Step title="Retrieve the result">
        Choose one delivery method:

        * **Webhooks** — wait for Seamless to `POST` the result to your endpoint. See [Receive research results with webhooks](/receive-research-results-with-webhooks).
        * **Polling** — call [Poll company research results](/pollcompanyresearchresults) with your `requestId` until `status` is `done`.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Contact">
    <Steps>
      <Step title="Authenticate">
        Use [OAuth](/authentication/oauth) (`Authorization: Bearer`) or an [API key](/authentication/api-keys) (`Token`). Store the credential.
      </Step>

      <Step title="Search contacts">
        Call [Search contacts](/searchcontacts) with the relevant filters.
      </Step>

      <Step title="Save the search result identifier">
        Identify the matching record and save its `searchResultId`.
      </Step>

      <Step title="Research contacts">
        Pass the `searchResultId` to [Research contacts](/researchcontacts). The API returns a `requestId`.
      </Step>

      <Step title="Save the request identifier">
        Save the `requestId`. Keep it separate from the `searchResultId`.
      </Step>

      <Step title="Retrieve the result">
        Choose one delivery method:

        * **Webhooks** — wait for Seamless to `POST` the result to your endpoint. See [Receive research results with webhooks](/receive-research-results-with-webhooks).
        * **Polling** — call [Poll contact research results](/pollcontactsresearchresults) with your `requestId` until `status` is `done`.
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="The agent repeats earlier steps unnecessarily">
    The agent is not persisting identifiers between steps. Store `searchResultId` and `requestId` as separate named values as soon as each is received. On subsequent steps, read the stored value instead of re-running the previous request. Re-running search when you already have a `searchResultId` wastes credits and introduces non-determinism.
  </Accordion>

  <Accordion title="The agent does not know whether to use polling or webhooks">
    Choose based on your infrastructure: use webhooks when your application runs a server that can receive inbound `POST` requests at a public HTTPS endpoint. Use polling when you want to check status on demand or cannot expose a public endpoint. If you are unsure, start with polling — it requires no additional setup. See [Choose the right workflow](/choose-the-right-workflow) for a full comparison.
  </Accordion>

  <Accordion title="The agent cannot continue after research starts">
    Confirm the `requestId` was saved immediately after the research response. If it was saved, continue with your chosen delivery method: call [Poll company research results](/pollcompanyresearchresults) or [Poll contact research results](/pollcontactsresearchresults) with the `requestId`, or wait for the webhook payload and match it against the stored `requestId`. Do not re-submit the research request.
  </Accordion>

  <Accordion title="Research returned an error status">
    Do not retry automatically. Return the `requestId` and poll `status` (for example `error` or `missing`) to the caller. Resubmit the research request only after confirming the input `searchResultId` is valid and the account has sufficient credits. Check credit balance in **Settings → Billing**.
  </Accordion>
</AccordionGroup>
