Skip to content

Test Case Import API

The import endpoints stage an export file and then commit it directly into a project — there is no preview or dry-run step. A successful commit creates (or replaces) test cases and suites in one request; any row that could not be imported is reported back individually, and the rest of the file still imports.

Probara is the default re-import platform. Re-importing a file this API previously exported (see the export API) is the zero-configuration path: the JSON, XML, CSV, and XLSX exports can each be re-imported, and the import preserves full fidelity. Qase and TestRail exports are also accepted, with a narrower field-mapping surface (see below). A Gherkin .feature file is accepted as gherkin_feature, one file per import (see Gherkin .feature files).

All requests require an authenticated request (Bearer API token or session cookie) carrying the import/export permission (held by owner/admin/member). Viewers receive 403 forbidden.

POST/api/v1/projects/{projectId}/imports:stage

{projectId} is the project code (for example ACME). The request body is multipart/form-data with a single file part carrying the export file.

200 with:

{ "uploadUlid": "01J...", "byteSize": 4821, "filename": "acme-export.json" }

uploadUlid identifies the staged upload for the commit call below. Staged uploads are not retained indefinitely — commit soon after staging.

StatusCodeCondition
403forbiddenThe caller’s role is viewer
413file_too_largeThe uploaded file exceeds IMPORT_MAX_FILE_BYTES (10 MB)
422validation_failedThe request carries no file part
429too_many_requestsThe organization’s stage rate limit was exceeded
POST/api/v1/projects/{projectId}/imports/commit
{
"uploadUlid": "01J...",
"sourceFormat": "probara_json",
"targetSuiteUlid": null,
"replaceMatching": false
}
FieldNotes
uploadUlidThe value returned by the stage call above.
sourceFormatOne of probara_json, probara_xml, probara_csv, probara_xlsx, qase_xml, qase_json, qase_csv, qase_xlsx, testrail_xml, testrail_csv, gherkin_feature. The web wizard resolves this automatically from the uploaded file’s extension/content — an integrator calling the API directly must set it explicitly.
targetSuiteUlidnull imports beneath the project root; otherwise the suite hierarchy in the file is recreated beneath the given suite.
replaceMatchingWhen true, a source case whose title exactly matches an existing case in the target suite overwrites that case’s content instead of creating a duplicate.

200 with the commit report:

{
"counts": {
"suitesCreated": 2,
"casesCreated": 8,
"casesReplaced": 1,
"rowsFailed": 0,
"reviewRequestsCreated": 0
},
"created": [{ "caseUlid": "01J...", "displayId": "ACME-42", "title": "Login succeeds" }],
"replaced": [],
"reviewRequests": [],
"failures": [],
"warnings": []
}

failures lists rows that did not import (the rest of the file still committed); warnings lists rows that did import but with a non-fatal notice — see below.

When the project’s test case review is mandatory (Pro or Enterprise plan), the commit writes no case. The suites are created; each case the import would create becomes a create review request, and each case it would replace an update request, with the caller as author and the project’s default reviewers. casesCreated and casesReplaced are 0, reviewRequestsCreated counts the requests, and reviewRequests lists them:

{
"reviewRequests": [
{
"reviewUlid": "01J...",
"displayId": "RV-7",
"kind": "update",
"caseUlid": "01J...",
"title": "Login succeeds"
},
{
"reviewUlid": "01J...",
"displayId": "RV-8",
"kind": "create",
"caseUlid": null,
"title": "Signup works"
}
]
}

A case is written only when its request is merged, with the same validation as POST .../test-cases (for example, required custom fields must have a value). Like the direct import, the commit is not all-or-nothing: requests opened before a later failure stay open.

Re-importing a Probara export (any of the four formats) maps back every system classification field — priority, severity, status, type, layer, behavior, is-flaky, preconditions, and postconditions — plus every existing user-defined custom field value, matched automatically by name (title for JSON/XML, slugified column header for CSV/XLSX). Steps, tags, description, and suite path always round-trip regardless of source platform. So do a case’s step format and each Gherkin step’s keyword.

No field or option is ever created on import. Every match is resolved against the organization’s existing custom fields and existing option values at commit time — there is no field-mapping payload in the request, and no interactive binding step. This makes re-import safe to run repeatedly (for example, restoring a backup or migrating cases between projects) without polluting the project’s schema.

The case’s automation key round-trips too. A replaced case keeps its current key when the file carries none, and an empty or whitespace-only key clears it. A key the import cannot apply — already held by another case of the project (archived included), repeated by an earlier row of the same file, or invalid — never fails the row: the case imports without it and the report carries an automation_key_skipped warning. Keys are checked against the project as it stood before the import, so a key the same file removes from one case cannot be given to another in that import.

The case’s parameters and excluded combinations round-trip too. CSV and XLSX carry them in two JSON columns, parameters and excluded_combinations, placed after the custom-field columns; JSON and XML carry them as parameters and excludedCombinations. The columns and keys appear only when a case has parameters or excluded combinations, so an export without them is unchanged. A shared parameter comes back as a local parameter with the values it held. The names parameters and excluded_combinations are reserved: a custom field with either title does not import its values. A Probara or .feature file that carries no parameters clears the parameters of a case it replaces, while a Qase or TestRail file, which carries none, keeps them.

Qase and TestRail exports map a narrower set — priority, type, and status only. User-defined custom fields are not mapped for those two platforms. A Gherkin .feature file maps no system or custom fields; its tags and suites are imported.

An import never fails a row just because one field’s value could not be mapped — instead it commits the case and records a warning:

Warning codeMeaning
unmapped_valueA system or user-defined option value did not match any existing option, and the field has no configured default — the field is left unset.
defaulted_valueSame as above, but the field has a configured default option — the field falls back to that default.
unmapped_fieldA user-defined field title (or CSV/XLSX column slug) did not match any field that exists on the project — the value is skipped, nothing is created.
automation_key_skippedThe automation key is already used by another case of the project, repeated by an earlier row, or invalid — the case imports without it.
shared_step_skippedThe case referenced a shared step whose text the file does not include — that reference is skipped and the case imports its other steps.
step_format_foldedThe case’s steps do not fit its stated format: a Gherkin step with no keyword or with an expected result, or a classic step with a keyword. The case imports as classic steps, each keyword kept at the start of its action.
parameters_skippedThe file’s parameters break a parameter rule, such as a repeated name or row, or more than 100 values for a single parameter — the case imports without them. A replace keeps the case’s existing parameters, and a .feature file’s Examples are not kept.
image_reference_droppedA step, description or paragraph value names an image that is not stored in this organization, as in a file exported from another organization — that image is removed and the rest of the text is imported.

Every row failure and warning names the affected sourceRow and case title, so a partially-successful import is fully diagnosable from the response alone.

A row fails (and its case is not imported) only for these reasons — every other value mismatch is the warning family above, never a failure:

CodeCondition
missing_titleThe row has no title.
ambiguous_matchreplaceMatching is set and the title matches more than one existing case in the target suite.
review_already_openMandatory review only: the case the row would replace already has an open review request.
review_proposal_invalidMandatory review only: the row’s content cannot be a review request (for example, a title over 400 characters or a step with no action).

Stage a .feature file as usual, then commit it with sourceFormat set to gherkin_feature. The file holds one Feature, written with English keywords. Each Scenario and each Scenario Outline becomes one Gherkin case under a suite named for the Feature, and a Rule adds a nested suite. The Background becomes the case’s preconditions. Tags become Probara tags, and the Feature’s and the Rule’s tags apply to every scenario under them. A Scenario Outline’s Examples become one parameter named Examples: the headers of its tables form the columns, in order of first appearance, and every row is kept, in order, with an empty cell where a table has no column. A table with a single column becomes a single parameter named after its header. Several Examples blocks merge into that one parameter, and their names and tags are not kept. When the Examples break a parameter rule, the case imports without them and the report carries a parameters_skipped warning. The .feature export writes a case’s parameters back as one Examples table, with a row for each combination the case runs. A Gherkin sync refuses such a scenario with 422, since a sync reports no warnings. A Feature or Rule description is not imported. A file the parser cannot read fails with 422 parse_failed, and its message names the line. The Gherkin test cases guide describes the same mapping for customers. A repository’s .feature files can instead be kept in sync with the Gherkin sync, so that their cases follow the files.

An import is bounded to IMPORT_MAX_CASES (5000) parsed cases and IMPORT_MAX_FILE_BYTES (10 MB) staged bytes. A file exceeding either ceiling is rejected before any suite or case is written.

Both stage and commit are rate-limited per organization (10 requests/hour each). Exceeding the limit returns 429 with { "error": { "code": "too_many_requests", ... } }.

Markdown is not, and has never been, an importable source format. A .md upload is rejected client-side by the wizard with a translated inline error; an API caller that stages a .md file and attempts to commit it with any sourceFormat receives 422 parse_failed — the file will not parse as the declared format.

StatusCodeCondition
403forbiddenThe caller’s role is viewer
404not_foundprojectId, targetSuiteUlid, or the staged uploadUlid does not resolve in the active organization
413file_too_largeThe staged bytes exceed IMPORT_MAX_FILE_BYTES (defense-in-depth re-check at commit time)
422parse_failedThe staged bytes did not parse as the declared sourceFormat, or the suite hierarchy nests too deeply; for gherkin_feature, the message names the line that failed
422empty_importThe file parsed to zero cases
422too_many_casesThe parsed case count exceeds IMPORT_MAX_CASES
429too_many_requestsThe organization’s import rate limit was exceeded

Stage a file, then commit it as a Probara JSON re-import:

Terminal window
curl -sS \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-F "file=@acme-export.json" \
"https://app.probara.net/api/v1/projects/ACME/imports:stage"
Terminal window
curl -sS \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"uploadUlid":"01J...","sourceFormat":"probara_json","targetSuiteUlid":null,"replaceMatching":false}' \
"https://app.probara.net/api/v1/projects/ACME/imports/commit"