Importing Data
Write records to an existing Form using the REST API or a CSV import. For the shared data model and ownership rules, start with Managing Data. To create identities first, follow Creating Members.
Before you begin
You need the target Workspace UUID, Form's DataType ID, published field slugs, and a credential authorized to create or update its records. Configure the Form with the Forms guide or the DataTypes API.
The examples assume a synthetic Form with a text field named status. Set
GRAVITYRAIL_API_KEY, GRAVITYRAIL_WORKSPACE_UUID, and
GRAVITYRAIL_DATA_TYPE_ID to your test configuration. Do not send patient data
until the PHI prerequisites are satisfied.
Create a record
curl --fail-with-body --request POST \
"https://api.gravityrail.com/api/v2/w/${GRAVITYRAIL_WORKSPACE_UUID}/data-types/${GRAVITYRAIL_DATA_TYPE_ID}/records" \
--header "Authorization: Bearer ${GRAVITYRAIL_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"fieldValues": {"status": "new"}}'
Use field slugs in fieldValues. The schema validates field types, required
values, and validation rules. Set memberId or memberExternalId when writing
for another Member; that requires the appropriate record-writing scope and access.
For workspace-scoped records, omit Member ownership.
Inspect the returned record ID and values, then verify the record in the Form's
entries. Use PUT /data-types/{data_type_id}/records/{record_id} to update a known
record by ID.
Update by external ID
Use the dedicated upsert endpoint when a repeated synchronization should update the same record:
curl --fail-with-body --request POST \
"https://api.gravityrail.com/api/v2/w/${GRAVITYRAIL_WORKSPACE_UUID}/data-types/${GRAVITYRAIL_DATA_TYPE_ID}/upsert/integration-record-001" \
--header "Authorization: Bearer ${GRAVITYRAIL_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"fieldValues": {"status": "active"}}'
A matching external ID updates the record; no match creates one. External-ID operations require the Form's external-ID permission as well as the applicable record access. A plain record-creation request is not a general upsert substitute.
For a JSON batch, use POST /data-types/{data_type_id}/records/bulk and its explicit
upsert option. The DataRecords API owns
batch schemas, limits, and per-record outcomes.
Import a CSV
For the product's upload controls, follow Import and export. For a programmatic upload, prepare headers using field names or slugs:
status,External ID
new,integration-record-001
active,integration-record-002
Then upload the file:
curl --fail-with-body --request POST \
"https://api.gravityrail.com/api/v2/w/${GRAVITYRAIL_WORKSPACE_UUID}/data-types/${GRAVITYRAIL_DATA_TYPE_ID}/import" \
--header "Authorization: Bearer ${GRAVITYRAIL_API_KEY}" \
--form "file=@records.csv"
Import begins when submitted. The response counts every data row once in
outcomes — created, updated, unchanged (matched a record and changed
nothing) and failed — alongside imported_count (created + updated +
unchanged), total_rows and up to ten row errors. success is false when any
row failed, and the response is still HTTP 200. Correct rejected rows before
resubmitting; rows that already committed come back unchanged. Keep logs to counts and record IDs;
row values and diagnostic response bodies can contain sensitive data.
Columns and matching
| Column | Use |
|---|---|
| A field name or slug | Map the cell to that field. A header matching multiple fields is rejected. |
Record ID | The record UUID a Gravity Rail export writes. Updates that record — only a record in this Form and workspace that you can edit. A Record ID matching no such record fails the row; it never falls back to the External ID or creates a record. A Record ID repeated later in the file fails the later rows. A numeric Record ID from an older export never selects a record: the row may still update its External ID match, and otherwise fails — export again. Any other non-blank Record ID (neither a record UUID nor a whole number) fails the row. A file with two Record ID columns is rejected whole. |
External ID | Without a Record ID: update the exact matching record, or create a record when it has no match. Beside a Record ID it must equal that record's External ID. Requires external-ID permission. |
Member ID | Set Member ownership for Member-scoped records. Writing for another Member requires records:write; workspace-scoped records reject this column's values. |
Created At, Updated At | Recognized export metadata; never written. |
Gravity Rail Export v1 | Preserve this marker on a Gravity Rail export so its encoded cells are read back correctly. |
An unmarked file trims surrounding whitespace. Blank cells omit the field: creation uses applicable defaults; updates retain existing values. A marked Gravity Rail export preserves its exported cell values and reverses spreadsheet formula escaping. Re-importing an unedited export updates each row's own record and creates nothing. To add copies of exported rows, clear their Record ID (or remove the column) and clear any External ID that matches an existing record; rows with neither create new records. Keep the marker when round-tripping lists and objects.
Import limits
| Rejection scope | Conditions |
|---|---|
| Whole file; no rows imported | Over 10 MB, over 10,000 data rows, over 1,000 columns, unknown or ambiguous headers, repeated columns for one field, or two Record ID columns. |
| Individual row | Invalid field values or validation rules, a Record ID that is neither a record UUID nor a whole number, values over 5,000 characters, External IDs over 255 characters, or conflicting record identity/ownership. |
A rejected row saves none of that row's values; other valid rows can succeed. Do not assume a partially successful file is an all-or-nothing transaction.
Verify a synchronization
Read back the returned record ID or list records through the DataRecords API, and check the intended values in the Form. Repeat one synthetic upsert with the same external ID: confirm that it updates the intended record before running a larger import.
For outgoing changes, use Exporting Data. To upload file bytes, use the Files API; a file URL in a Form field does not upload its content.