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

# Create and update contacts

> Choose the right contact write endpoint, understand upsert matching, and structure advanced fields for contact create and update workflows.

## Which endpoint should you use?

For most use cases, use `POST /contacts/upsert`. It handles both creating and updating contacts in a single call, with automatic deduplication based on ID, email, or name/birthdate/address.

For specific scenarios, two synchronous alternatives are available:

* `POST /contacts` — creates a single contact and returns it immediately; useful when you need the new contact's ID right away.
* `PATCH /contacts/{id}` — updates a known contact by ID; useful for targeted edits when you already have the whole contact object and ID.

## Write payload shapes by endpoint

`POST /contacts/upsert` accepts high-level contact payloads. For advanced fields, use field-specific arrays such as `consents`, `status`, `forms`, `custom_fields`, `actions`, or `presence_status`.

Use `POST /contacts` or `PATCH /contacts/{id}` when you need synchronous create or update behavior. For advanced fields, both endpoints use the lower-level `formdatas` structure to link a contact to a form and a selected value.

## Upsert matching strategy

When upserting, the system tries to find an existing contact to update using the following strategy (in order). If no match is found, a new contact is created.

<Steps>
  <Step title="Match by ID">
    If an `id` is provided and matches an existing contact, that contact is updated. Accepts a Qomon contact ID.
  </Step>

  <Step title="Match by NationBuilder ID">
    If no `id` match was found, the system looks for a contact with the same `nationbuilderid`.
  </Step>

  <Step title="Match by external ID">
    If no NationBuilder ID match was found, the system looks for a contact with the same `external_id` — a free-form identifier you can use to reference your own system's ID for this contact.
  </Step>

  <Step title="Match by email + name">
    If no ID is provided (or it didn't match), the system searches for contacts with the same `mail`. Among the results, `firstname` and `surname` are used as additional filters if provided. An empty value means the field is not used for matching.
  </Step>

  <Step title="Match by surname + firstname + birthdate + address">
    If no email match was found and a `surname` is provided, the system searches by surname. `firstname` must also match. If both contacts have a `birthdate`, it must match exactly. If both have an `address`, `city` or `postalcode` must match — and if no birthdate comparison was made, `street` must also match.
  </Step>

  <Step title="No match — contact is created">
    If none of the above strategies found a match, a new contact is created.
  </Step>
</Steps>

<Warning>
  If the `id` or `label` of an advanced field, or the `value` (for non-free-text fields), does not exist, the contact will not be updated or created. **Errors will not be logged.**
</Warning>

## Advanced fields (form fields)

In `POST /contacts/upsert`, several fields in the `contact` object accept **arrays of form objects**. Each object requires a form `id` or unique `label`, and a string `value` defined in your account settings.

<Steps>
  <Step title="Find your form IDs">
    Each form type has its own endpoint. Call the relevant one to retrieve the IDs and accepted values for your account:

    | Field           | Endpoint                                                |
    | :-------------- | :------------------------------------------------------ |
    | `consents`      | `GET https://incoming.qomon.app/forms/consent`          |
    | `custom_fields` | `GET https://incoming.qomon.app/forms/custom_fields`    |
    | `status`        | `GET https://incoming.qomon.app/forms/level_of_support` |

    For more information, look at [the reference for /forms](/api-reference/forms/retrieve-forms-by-their-type).
  </Step>

  <Step title="Retrieve your form IDs">
    Each field type has a dedicated endpoint. Call it to get the valid IDs and accepted values for your account:

    | Field family     | Payload key       | Endpoint                              |
    | :--------------- | :---------------- | :------------------------------------ |
    | Consents         | `consents`        | `GET /v1/forms/type/consent`          |
    | Custom fields    | `custom_fields`   | `GET /v1/forms/type/custom_fields`    |
    | Level of support | `status`          | `GET /v1/forms/type/level_of_support` |
    | Survey answers   | `forms`           | `GET /v1/forms/type/survey`           |
    | Tasks            | `actions`         | `GET /v1/forms/type/tasks`            |
    | Presence status  | `presence_status` | `GET /v1/forms/type/presence_status`  |

    See the [Forms overview](/pages/v1/reference/forms/overview) for more details about form types and `formdatas`.
  </Step>

  <Step title="Correct payload structure">
    Each form field follows the same shape: an array of objects with an id and a string value.

    If the form has a **unique** label, you can use the label instead of the id.

    ```json theme={"system"}
    {
      "kind": "contact",
      "data": {
        "firstname": "Jane",
        "surname": "Doe",
        "gender": "F",
        "mail": "jane.doe@example.com",
        "address": {
          "postalcode": "12345"
        },
        "consents": [
          { "id": 1234, "value": "consent_email" },
          { "label": "General communication", "value": "consent_sms" }
        ],
        "custom_fields": [
          { "id": 4512, "value": "member" }
        ],
    	"status": [
    	  { "id": 9826, "value": "donator" }
    	]
      }
    }
    ```
  </Step>

  <Step title="Structure your payload">
    Each form field is an array of objects with an `id` and a `value`. If a form has a **unique** label, you can use `label` instead of `id`.

    ```json theme={"system"}
    {
      "kind": "contact",
      "data": {
        "firstname": "Jane",
        "surname": "Doe",
        "mail": "jane.doe@example.com",
        "consents": [
          { "id": 1234, "value": "consent_email" },
          { "label": "General communication", "value": "consent_sms" }
        ],
        "custom_fields": [
          { "id": 4512, "value": "member" }
        ],
        "status": [
          { "id": 9826, "value": "donator" }
        ],
        "forms": [
          { "id": 7621, "value": "Interested in volunteering" }
        ],
        "actions": [
          { "id": 2314, "value": "send_information" }
        ]
      }
    }
    ```
  </Step>
</Steps>

<Info>
  Use the form `id` returned by the endpoint, not the `form_ref_id`.
</Info>

<Info>
  The `value` field always expects a string — never a boolean or integer.
</Info>

<Warning>
  Every form field must be an array, even when setting a single value.
</Warning>

## Related reference pages

* See [Handle form structure](/pages/v1/reference/forms/overview) to decode `formdatas` values when you fetch contacts.
* See [Contacts overview](/pages/v1/reference/contacts/overview) for the contact object model.
