# Contact (https://webmetic.de/docs/en/api/datenmodell/contact)

For example, if someone from Bergquell Mineralbrunnen reads your pricing page several times, `/contacts/search` finds their head of marketing. Contacts originate from the contact search, not from tracking.

## Rules [#rules]

These rules apply to every contact:

- **Visiting companies only**: The `company_id` must belong to a company that visited one of your websites. Otherwise `search`, `lookup`, and `find` answer with `COMPANY_NOT_IDENTIFIED`. The API doesn't search by domain, name, or person.
- **Setup first**: You set up contacts for your account once in the dashboard, and until then `search`, `reveal`, and `find` answer with `CONTACTS_SETUP_REQUIRED`. For details, see [Set up contacts](https://webmetic.de/docs/en/ansprechpartner/einrichten).
- **Credits**: Search and lookup cost no credits. `reveal` and `find` cost credits, but only for the data Webmetic delivers. For details, see [Credits in the API](https://webmetic.de/docs/en/api/credits).
- **One account, one pool**: Unlocked data belongs to your account, so all users, the dashboard, the API, and the AI assistant see it at no extra cost. Another Webmetic account doesn't see it.
- **Free plan**: For companies hidden on the Free plan, `search`, `lookup`, and `find` answer with `PLAN_REQUIRED`. For details, see [Plans](https://webmetic.de/docs/en/konto/plaene).
- **Access**: If your partner turned off the **Data profiles** feature, the API answers with `FEATURE_DISABLED`.

All error codes are listed under [Errors](https://webmetic.de/docs/en/api/fehler).

## Fields [#fields]

This is how `/contacts/search` and `/contacts/lookup` return a contact in the `contacts` list:

| Field | Type | Example | Description |
| --- | --- | --- | --- |
| `contact_id` | `string` | `"v1.q7Rk2mXv9TnL4w"` | Permanent ID of the person, which you pass to `/contacts/reveal`. Never `null`. Treat the ID as text without a fixed format |
| `name` | `string` or `null` | `"Miriam Eckhardt"` | First and last name |
| `job_title` | `string` or `null` | `"Leiterin Marketing"` | Position at the company |
| `company_name` | `string` or `null` | `"Bergquell Mineralbrunnen GmbH"` | Employer as the contact data has it. Can differ from the `company_name` of the [Company](https://webmetic.de/docs/en/api/datenmodell/company) |
| `has_email` | `boolean` | `true` | An email address is on file |
| `has_phone` | `boolean` | `true` | A phone number is on file |
| `has_social_link` | `boolean` | `true` | A LinkedIn profile is on file |
| `email` | `string` or `null` | `"m.eckhardt@bergquell.example"` | Email address, after the email is unlocked |
| `linkedin` | `string` or `null` | `"https://www.linkedin.com/in/miriam-eckhardt-example"` | URL of the LinkedIn profile, after the email is unlocked |
| `direct_phone` | `string` or `null` | `null` | Direct line or work number, after the phone is unlocked |
| `mobile_phone` | `string` or `null` | `null` | Mobile number, after the phone is unlocked |

The contact doesn't include the person's department, level, or location, even if you filter by department or level.

## Fields until unlocked [#fields-until-unlocked]

Four fields stay empty until your account unlocks the data:

- **Email**: Unlocking fills `email` and `linkedin`.
- **Phone**: Unlocking fills `direct_phone` and `mobile_phone`.

The search always returns these four fields as `null`, even for people your account has already unlocked. `/contacts/lookup` includes unlocked data and leaves out empty fields entirely. If your account hasn't searched this company with these filters yet, the lookup answers with exactly `{"exists": false}`.

## Contact in reveal and find [#contact-in-reveal-and-find]

After unlocking, the contact has a different shape:

| Endpoint | Shape | Fields of the person |
| --- | --- | --- |
| `/contacts/reveal` | Object `contact` | `contact_id`, `email`, `linkedin`, `direct_phone`, `mobile_phone`. Anything your account hasn't unlocked is `null` |
| `/contacts/find` | Flat in the response, with `found` | `contact_id`, `name`, `job_title`, `company_name`, `email`, `linkedin`, `direct_phone`, `mobile_phone`. With `found: false`, all are `null` |

`search`, `reveal`, and `find` also return `credits_used`, the credits of this call, and `credits_remaining`, the credits your account has afterwards.

## Country of the results [#country-of-the-results]

Webmetic searches for people in the company's country. `country` names that country as an ISO code with two capital letters, for example `DE`.

If the search finds nobody there, Webmetic searches worldwide and sets `country_fallback` to `true`. If no country is known, `country` is `null` and the search also runs worldwide.

Both fields sit in the response of `search`, `lookup`, and `find`, not in the single contact. The lookup leaves out `country` when it's `null`.

## Pages [#pages]

The pages of `/contacts/search` start at 0. A page holds 10 to 25 people, with 25 as the default. `total` counts all matching people, not only the ones on this page. In the lookup the field is called `total_results`, and the lookup returns only the first page. For details, see [Pagination](https://webmetic.de/docs/en/api/paginierung).

## Example [#example]

The example shows a search at Bergquell Mineralbrunnen before unlocking:

```json title="POST /contacts/search"
{
  "total": 14,
  "page": 0,
  "contacts": [
    {
      "contact_id": "v1.q7Rk2mXv9TnL4w",
      "name": "Miriam Eckhardt",
      "job_title": "Leiterin Marketing",
      "company_name": "Bergquell Mineralbrunnen GmbH",
      "has_email": true,
      "has_phone": true,
      "has_social_link": true,
      "email": null,
      "linkedin": null,
      "direct_phone": null,
      "mobile_phone": null
    }
  ],
  "from_cache": false,
  "country": "DE",
  "country_fallback": false
}
```

The response is shortened: `credits_used` and `credits_remaining` are left out. `from_cache: true` means the result comes from a stored search.

## Edge cases [#edge-cases]

- **`404` when unlocking**: If the person isn't from a search of your account or can no longer be found, `/contacts/reveal` answers with `CONTACT_NOT_FOUND`. Search again and use the `contact_id` from the new result.
- **Data that can't be given out**: For some people, Webmetic may not give out contact details. The API then answers with `404` and deducts no credits.
- **Unlocking again**: `/contacts/reveal` returns data your account has already unlocked again, at no cost.
- **Empty despite `has_email`**: If Webmetic finds no address when unlocking, `email` stays `null`. That data type then costs nothing and stays open.
- **Company without a website**: If no website is on file for the company, `search`, `lookup`, and `find` answer with `404` and `COMPANY_WITHOUT_WEBSITE`. The API returns no contacts for this company.
- **No match in find**: If nobody fits, `/contacts/find` answers with `found: false` and deducts nothing.

## Used by [#used-by]

- **Endpoints**: [`POST /contacts/search`](https://webmetic.de/docs/en/api/referenz/contacts/search) to search, [`GET /contacts/lookup`](https://webmetic.de/docs/en/api/referenz/contacts/lookup) for stored searches with unlocked data, [`POST /contacts/reveal`](https://webmetic.de/docs/en/api/referenz/contacts/reveal) to unlock, and [`POST /contacts/find`](https://webmetic.de/docs/en/api/referenz/contacts/find) to find and unlock in one call
- **AI assistant**: The [AI assistant](https://webmetic.de/docs/en/ki-assistent/tools-und-prompts) also finds and unlocks contacts, with the tools `find_contacts` and `reveal_contact`.
