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

# ETL: OneTouch Health Destination

> Create or update carers in OneTouch Health from an Integrate.io ETL pipeline, with field mapping, throttling, and failed-record CSV outputs.

Use the OneTouch Health destination to write carer records from your ETL pipeline to OneTouch Health through the OneTouch Connect C3 API. Each input record becomes one request to OneTouch. The component either creates a new carer or updates an existing carer matched on `external_id`.

## When to use it

Use this destination to sync carers from an HR system, recruitment tool, or data warehouse into OneTouch. Use **Create** to onboard new carers and **Update** to keep existing carer details in step with your source system.

To read carers and other OneTouch objects into a pipeline, use the [OneTouch Health source](/docs/etl/using-components-onetouch-health-source) instead.

## Connection

Select an existing OneTouch Health connection or create a new one. The destination uses the same connection as the source. See [OneTouch Health connection setup](/docs/etl/using-components-onetouch-health-source#connection-setup).

Connect with **Username & password** for any job that may run longer than about 15 minutes. OneTouch tokens expire after roughly 15 to 20 minutes. With a username and password, Integrate.io logs in again when the token expires. With an API token, the job fails once the token expires.

A package can read from and write to OneTouch on the same connection. OneTouch revokes a user's previous token on every new login. To avoid revoking each other's token, the source and destination share the most recent token. Don't share the OneTouch user with other tools or environments. See the [dedicated user warning](/docs/etl/using-components-onetouch-health-source#choosing-how-to-connect).

### Master key or location-restricted key

The kind of OneTouch key behind the user decides which location fields a record needs:

| Key | What to map |
| :- | :- |
| Location-restricted key | Nothing extra. New carers go to the system-generated **API Imported** area unless you map `area`. |
| Master key | `location` or `branch` on **Create** and on **Update**, and `area` on **Create**. Without them, OneTouch rejects the record with "The location field is required when branch is not present." |

## Destination properties

* **OneTouch object** - the object to write. **Carers** is the only object. Changing the object clears the field mapping.
* **Operation** - what the component does with each record:

  * **Create** (default) - posts each record as a new carer. OneTouch rejects a record whose `external_id` already belongs to a carer.
  * **Update (matched on external\_id)** - updates the carer with the matching `external_id`. Empty values are not sent. With a master key, Update also needs `location` or `branch`.

  Both operations use the same fields, so changing the operation keeps your field mapping.

### Advanced options

* **Requests per second** - throttle applied to the OneTouch API. Default is `8`, which is OneTouch's documented limit. Accepts values from `0.1` to `8`. Each record is one request. Lower this value if other integrations use the same OneTouch login.
* **Maximum errors** - the job fails once the number of failed records exceeds this value. Default is `0` (fail on the first failed record). Set to `-1` to allow unlimited failures.
* **Output failed records (CSV)** - writes rejected records to a CSV. Select an **Errors output connection** and enter an **Errors output path**.
* **Output written records (CSV)** - writes records that OneTouch accepted to a CSV. Select a **Written records output connection** and enter a **Written records output path**.

## Field mapping

Map input fields to OneTouch carer fields. The mapping step starts with rows for the three required fields, without an input field selected. Click **Auto-fill** to map every input field whose name matches a carer field. You can't complete the step until every required field has an input field selected.

**Required fields** (both operations):

| Field | Description |
| :- | :- |
| `external_id` | Your own unique ID for the carer. **Update** uses it to find the carer. |
| `firstname` | Carer's first name. |
| `lastname` | Carer's last name. |

**Optional fields:**

| Field | Format |
| :- | :- |
| `title`, `known_as`, `sex`, `comment` | Text. |
| `dob`, `date_start` | Date in `yyyy-mm-dd` format. |
| `pps`, `ni_number` | Text. Both write the same OneTouch field, so map only one of them. |
| `primary_email`, `secondary_email` | Email address. |
| `phone_mobile`, `phone_home`, `phone_work` | Text. |
| `address`, `town`, `county`, `postcode`, `country`, `nationality` | Text. |
| `transport_type`, `recruitment_source`, `position`, `location`, `branch`, `area`, `status` | A OneTouch ID or exact name. See [IDs and names](#ids-and-names). |
| `bank_account_name`, `bank_sort_code`, `bank_account_number`, `bank_iban` | Text. |
| `unique_email` | `true` or `false` (also accepts `1`/`0` and `yes`/`no`). Requires `primary_email`. |

Integrate.io checks each record before sending it. A record fails without a request to OneTouch when a required field is empty, a date isn't in `yyyy-mm-dd` format, `unique_email` isn't a boolean value, or `unique_email` is `true` without a `primary_email`. Failed records count toward **Maximum errors**.

When you save or validate the package, Integrate.io also checks the mapping. Validation fails when the mapping:

* Leaves out `external_id`, `firstname`, or `lastname`.
* Includes a field that isn't in the carer field list above.
* Maps the same carer field more than once.
* Maps both `pps` and `ni_number`.

To look up valid IDs for positions, areas, statuses, locations, and transport types, read the matching lookup table with the [OneTouch Health source](/docs/etl/using-components-onetouch-health-source#source-table-object).

### IDs and names

`transport_type`, `recruitment_source`, `position`, `location`, `branch`, `area`, and `status` take either an ID or a name. OneTouch tells them apart by the type of value it receives, and the component sends each value with the type of its input field:

| Input field type | Sent as | OneTouch reads it as |
| :- | :- | :- |
| Number, for example `3340` | A number. A whole decimal such as `12.0` is sent as `12`. | An ID |
| Text, for example `'3340'` | Text | A name |

An ID held in a text field therefore fails, typically with "Location not found". The OneTouch Health source can return IDs as text, so cast them to a number before the destination, for example in a [Select](/docs/etl/using-components-select-transformation) transformation:

```
(int)location_id
```

`status` takes a status configured in your OneTouch account, by its name or ID (read the **CarerStatuses** object to list them). It does not take a status type: "Archived" fails with "No mathching status found" (OneTouch's spelling) unless a status is named exactly that. To archive a carer, run **Update** with `status` set to a status whose type is Archived.

## Success and error tracking

Both CSV outputs are optional and configured independently. Supported storage connections are Amazon S3, Google Cloud Storage, Azure Blob Storage, SFTP, and FTPS.

* The failed records CSV has an `Error` column with OneTouch's error message, a `Request JSON` column with the payload that was sent, and one column per mapped field.
* The written records CSV has a `OneTouch Id` column with the carer's OneTouch ID, followed by one column per mapped field.

If you turn on an output, you must select a connection for it. Otherwise package validation fails.

## Retries

Integrate.io retries a request when OneTouch returns HTTP 429 or a 5xx error, or doesn't answer in time, and honors the `Retry-After` header up to 60 seconds. If a 429 or 5xx persists through every retry, the record counts as failed. If three records in a row fail this way, the job stops, because OneTouch is likely unavailable. If OneTouch doesn't answer at all after every retry, the job stops straight away.

Other errors, such as a duplicate `external_id`, an unknown position, or a missing `location` or `branch` with a master key, fail only that record. The failed records CSV shows OneTouch's message with its per-field details. An expired token stops the job on an **API token** connection; with **Username & password**, Integrate.io logs in again and continues.

A retried **Create** never creates a second carer. OneTouch rejects a duplicate `external_id`. If the first attempt may have succeeded, Integrate.io looks up the carer by `external_id` and reports the record as written when OneTouch holds the values that were sent.

Integrate.io doesn't retry failed job tasks for this destination. A retried task would resend carers the failed attempt already wrote, and OneTouch would reject them as duplicates. A task the cluster kills, for example when its node is lost, still runs again and resends its records. If a job fails, check the failed records CSV before you run it again.

## Best practices

* **Run jobs on a recently created cluster.** Clusters get the OneTouch destination connector when they are created. Create a new cluster before you run your first OneTouch destination job.
* **Test on a small sample first.** Each record that OneTouch accepts creates or changes a real carer.

## Example: syncing new carers from a warehouse query

A pipeline that reads carers hired in the last day and creates them in OneTouch:

```sql theme={null}
-- Database source query
SELECT
  employee_id          AS external_id,
  first_name           AS firstname,
  last_name            AS lastname,
  email                AS primary_email,
  mobile               AS phone_mobile,
  TO_CHAR(start_date, 'YYYY-MM-DD') AS date_start,
  job_title            AS position,
  onetouch_location_id AS location,
  onetouch_area_id     AS area
FROM employees
WHERE role = 'carer'
  AND hired_at >= CURRENT_DATE - 1
```

Set **Operation** to **Create**, then click **Auto-fill** in the mapping step to map each aliased column to the carer field of the same name. `location` and `area` are needed with a master key; they come from integer columns, so they are sent as IDs. `position` is sent as a name and must match an existing OneTouch position exactly. Turn on **Output failed records (CSV)** with an S3 connection so you can review rejected records after the run. Use a second component with **Update** in a separate package to push changes to existing carers.

## FAQ

**Q: Why did a record fail with "The location field is required when branch is not present"?**

The connection uses a master key, and OneTouch needs `location` or `branch` on every create and every update. Map one of them on the component.

**Q: Why did a record fail with "Location not found"?**

The `location` value reached OneTouch as text, so OneTouch looked for a location with that name. Cast the ID to a number, for example `(int)location_id`. See [IDs and names](#ids-and-names).

**Q: Why did a record fail with "No mathching status found"?**

`status` takes a status configured in your account, not a status type such as "Archived". Read the **CarerStatuses** object with the [OneTouch Health source](/docs/etl/using-components-onetouch-health-source) to list the valid names and IDs.

## Related

<CardGroup cols={2}>
  <Card title="OneTouch Health Source" icon="arrow-right" href="/docs/etl/using-components-onetouch-health-source" horizontal />

  <Card title="REST API Destination" icon="arrow-right" href="/docs/etl/using-components-rest-api-destination" horizontal />

  <Card title="Defining Connections" icon="arrow-right" href="/docs/etl/defining-connections" horizontal />

  <Card title="Destinations Overview" icon="arrow-right" href="/docs/etl/category/destinations" horizontal />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.