# Contacts, companies and external ids

Map the user and company ids of your own system as external ids; create or update contacts by external id, link the company, send usage and revenue signals.

URL: https://getclickfix.com/docs/integration-guide/contacts-companies?lang=en

## External ids: contact and company [#external-id]

The ids of your own system live in ClickFix as the **external id** (`external_id`). Contact and company external ids are **separate fields**, independent of each other and unique within the organization:

| Record  | API field     | Widget `identify` counterpart | In system binding       |
| ------- | ------------- | ----------------------------- | ----------------------- |
| Contact | `external_id` | `user_id`                     | `{external_id}`         |
| Company | `external_id` | `company.id`                  | `{company_external_id}` |

`PUT /v1/public-api/contacts/by-external-id` creates or updates the contact by external id (`201` created, `200` updated). Matching is by external id only; no other contact is taken over through e-mail or phone. An id that belongs to another contact returns `409 EXTERNAL_ID_EXISTS`. If you create the contact through the API first and the user later arrives through `identify`, no new contact is opened: the user is linked to the same contact, and a conversation an agent started before the customer visited the site (with `contact_external_id`) appears in the widget once the user signs in.

{/* example: widget/02-external-id.js */}

```js tab="Widget"
// Adım 2: kişi ve şirket için ayrı dış kimlik. user_id kişinin, company.id şirketin.
// @ts-check
ClickFix("identify", {
  user_id: "1001", // API: contacts.external_id
  user_hash: "SUNUCUDA_HESAPLANAN", // user_id + "\n" + company.id üzerinden imzalı
  company: { id: "F-77", name: "Acme A.Ş.", domain: "acme.com.tr" }, // API: companies.external_id
});
ClickFix("on", "identify", ({ verified, company_verified }) =>
  console.log("doğrulandı:", verified, "firma:", company_verified),
);
```

{/* example: node/02-external-id.ts */}

```ts tab="Node.js"
// Adım 2: kişiyi dış kimliğiyle oluştur/güncelle, şirketi dış kimliğiyle bul.
import { createClient } from "@getclickfix/node";

const client = createClient({
  baseURL: process.env.CLICKFIX_BASE_URL!, // https://api.getclickfix.com
  apiKey: process.env.CLICKFIX_API_KEY!, // contacts:read, contacts:write, companies:read
});

// Aynı dış kimlikle ikinci çağrı yeni kişi açmaz, günceller (200); ilki 201 döner.
const contact = await client.request(
  "/v1/public-api/contacts/by-external-id",
  "put",
  { external_id: "1001", name: "Ayşe Yılmaz", email: "ayse@example.com" },
  { idempotencyKey: `user-1001-${Date.now()}` },
);

// Şirketin dış kimliği kişininkinden ayrıdır (widget: company.id).
const companies = await client.request(
  "/v1/public-api/companies",
  "get",
  undefined,
  { query: { external_id: "F-77" } },
);

console.log(
  JSON.stringify({ contact: contact.data, companies: companies.data }),
);
```

{/* example: go/external_id.go */}

```go tab="Go"
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	clickfix "github.com/ClickSolveMainTR/clickfix-sdks/go"
)

// Adım 2: kişiyi dış kimliğiyle oluştur/güncelle, şirketi dış kimliğiyle bul.
func externalID(ctx context.Context) error {
	client, err := clickfix.New(os.Getenv("CLICKFIX_BASE_URL"), os.Getenv("CLICKFIX_API_KEY"))
	if err != nil {
		return err
	}
	name, email := "Ayşe Yılmaz", "ayse@example.com"
	key := fmt.Sprintf("user-1001-%d", time.Now().UnixNano())
	res, err := client.PutV1PublicApiContactByExternalIdWithResponse(ctx,
		&clickfix.PutV1PublicApiContactByExternalIdParams{IdempotencyKey: key},
		clickfix.PutV1PublicApiContactByExternalIdJSONRequestBody{ExternalId: "1001", Name: &name, Email: &email})
	if err != nil {
		return err
	}
	if res.StatusCode() != 200 && res.StatusCode() != 201 { // 200 güncellendi, 201 oluşturuldu
		return fmt.Errorf("clickfix: HTTP %d", res.StatusCode())
	}
	ext := "F-77" // şirketin dış kimliği (widget identify: company.id)
	companies, err := client.GetV1PublicApiCompaniesWithResponse(ctx, &clickfix.GetV1PublicApiCompaniesParams{ExternalId: &ext})
	if err != nil {
		return err
	}
	return json.NewEncoder(os.Stdout).Encode(map[string]any{"contact": res.StatusCode(), "companies": companies.StatusCode()})
}
```

{/* example: php/02-external-id.php */}

```php tab="PHP"
<?php
// Adım 2: kişiyi dış kimliğiyle oluştur/güncelle, şirketi dış kimliğiyle bul.
require __DIR__.'/vendor/autoload.php'; // composer require clickfix/sdk guzzlehttp/guzzle

use ClickFix\Client;

$client = new Client(
    getenv('CLICKFIX_BASE_URL'), // https://api.getclickfix.com
    getenv('CLICKFIX_API_KEY'),  // contacts:read, contacts:write, companies:read
);

// Aynı dış kimlikle ikinci çağrı yeni kişi açmaz, günceller (200); ilki 201 döner.
$contact = $client->contacts()->upsertByExternalId(
    '1001',
    ['name' => 'Ayşe Yılmaz', 'email' => 'ayse@example.com'],
    idempotencyKey: 'user-1001-'.(int) (microtime(true) * 1000),
);

// Şirketin dış kimliği kişininkinden ayrıdır (widget: company.id).
$company = $client->companies()->findByExternalId('F-77');

echo json_encode(['contact' => $contact, 'company' => $company]), PHP_EOL;
```

{/* example: curl/02-external-id.sh */}

```bash tab="curl"
# Adım 2: kişiyi dış kimliğiyle oluştur/güncelle (201 oluşturuldu, 200 güncellendi).
curl -sS --fail-with-body -X PUT "$CLICKFIX_BASE_URL/v1/public-api/contacts/by-external-id" \
  -H "Authorization: Bearer $CLICKFIX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: user-1001-$(date +%s)" \
  -d '{ "external_id": "1001", "name": "Ayşe Yılmaz", "email": "ayse@example.com" }'

# Şirketi kendi dış kimliğiyle bul (kişi dış kimliğinden ayrıdır).
curl -sS --fail-with-body "$CLICKFIX_BASE_URL/v1/public-api/companies?external_id=F-77" \
  -H "Authorization: Bearer $CLICKFIX_API_KEY"
```

The `identity_state` field of contact and conversation responses is `verified`, `anonymous` or `self_named`; webhook events carry `contact_external_id`, `contact_verified`, `company_uuid` and `company_external_id`, so you match your own record without another request.

## Company [#company]

A company is created or matched through `company` in the widget's `identify` call: the first key is `company.id` (the company's external id; used only when `user_hash` signs it too, see [identity verification](/docs/integration-guide/identity?lang=en)), the second is `domain`. A company bound through a signed id carries the **Verified** badge in the panel. Through the API a company is found with `GET /v1/public-api/companies?external_id=`; three customer-health signals are sent against it: usage (`PUT /company-usage`, 0–100), revenue (`PUT /company-revenue`) and product events (`POST /company-events`, next section). In all three the company is selected by `company_uuid`, `external_id` or `domain`.

{/* example: widget/04-company.js */}

```js tab="Widget"
// Adım 4: şirket identify ile oluşur ya da dış kimliğiyle eşleşir (ikinci anahtar: domain).
// company.id yalnız user_hash onu da imzalıyorsa bağlanır (user_id + "\n" + company.id).
// @ts-check
ClickFix("boot", {
  widgetId: "WIDGET_ID",
  user: {
    user_id: "1001",
    user_hash: "SUNUCUDA_HESAPLANAN", // user_id + "\n" + "F-77" üzerinden
    company: { id: "F-77", name: "Acme A.Ş.", domain: "acme.com.tr" },
  },
});
```

{/* example: node/04-company.ts */}

```ts tab="Node.js"
// Adım 4: şirket. Şirket widget identify (company.id) ile oluşur; API ile
// bulunur ve müşteri sağlığı sinyalleri (kullanım, gelir, olay) gönderilir.
import { createClient } from "@getclickfix/node";

const client = createClient({
  baseURL: process.env.CLICKFIX_BASE_URL!,
  apiKey: process.env.CLICKFIX_API_KEY!, // companies:read, health:write
});

const found = await client.request(
  "/v1/public-api/companies",
  "get",
  undefined,
  {
    query: { external_id: "F-77" },
  },
);

// 0-100 arası kullanım sinyali; 30 gün boyunca sağlık puanına katılır.
const usage = await client.request(
  "/v1/public-api/company-usage",
  "put",
  { external_id: "F-77", score: 82 },
  // İşlemi tanımlayan sabit değer: kendi ölçüm kaydınızın kimliği (örnekte zaman damgası)
  { idempotencyKey: `usage-F-77-${Date.now()}` },
);

console.log(JSON.stringify({ found: found.data, usage: usage.data }));
```

{/* example: go/company.go */}

```go tab="Go"
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	clickfix "github.com/ClickSolveMainTR/clickfix-sdks/go"
)

// Adım 4: şirketi bul ve kullanım sinyali gönder (health:write).
func company(ctx context.Context) error {
	client, err := clickfix.New(os.Getenv("CLICKFIX_BASE_URL"), os.Getenv("CLICKFIX_API_KEY"))
	if err != nil {
		return err
	}
	ext := "F-77"
	found, err := client.GetV1PublicApiCompaniesWithResponse(ctx, &clickfix.GetV1PublicApiCompaniesParams{ExternalId: &ext})
	if err != nil {
		return err
	}
	usage, err := client.PutV1PublicApiCompanyUsageWithResponse(ctx,
		// İşlemi tanımlayan sabit değer: kendi ölçüm kaydınızın kimliği (örnekte zaman damgası)
		&clickfix.PutV1PublicApiCompanyUsageParams{IdempotencyKey: fmt.Sprintf("usage-F-77-%d", time.Now().UnixNano())},
		clickfix.PutV1PublicApiCompanyUsageJSONRequestBody{ExternalId: &ext, Score: 82})
	if err != nil {
		return err
	}
	if usage.StatusCode() != 200 {
		return fmt.Errorf("clickfix: HTTP %d", usage.StatusCode())
	}
	return json.NewEncoder(os.Stdout).Encode(map[string]int{"found": found.StatusCode(), "usage": usage.StatusCode()})
}
```

{/* example: php/04-company.php */}

```php tab="PHP"
<?php
// Adım 4: şirket. Şirket widget identify (company.id) ile oluşur; API ile
// bulunur ve müşteri sağlığı sinyalleri (kullanım, gelir, olay) gönderilir.
require __DIR__.'/vendor/autoload.php';

use ClickFix\Client;

$client = new Client(
    getenv('CLICKFIX_BASE_URL'),
    getenv('CLICKFIX_API_KEY'), // companies:read, health:write
);

$found = $client->companies()->findByExternalId('F-77');

// 0-100 arası kullanım sinyali; 30 gün boyunca sağlık puanına katılır.
$usage = $client->companies()->setUsage(
    ['external_id' => 'F-77', 'score' => 82],
    // İşlemi tanımlayan sabit değer: kendi ölçüm kaydınızın kimliği (örnekte zaman damgası)
    idempotencyKey: 'usage-F-77-'.(int) (microtime(true) * 1000),
);

echo json_encode(['found' => $found, 'usage' => $usage]), PHP_EOL;
```

{/* example: curl/04-company.sh */}

```bash tab="curl"
# Adım 4: şirket kullanım sinyali (0-100, health:write); şirket external_id ile bulunur.
curl -sS --fail-with-body -X PUT "$CLICKFIX_BASE_URL/v1/public-api/company-usage" \
  -H "Authorization: Bearer $CLICKFIX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: usage-F-77-$(date +%s%N)" \
  -d '{ "external_id": "F-77", "score": 82 }'
```
