Import a service's price table from CSV
AI agents: read /llms.txt; add .md to any page URL. This page as Markdown: /reference/csv-imports/v1-imports-csv-store.md. Index of every page and the rules for every call: /llms.txt. Guides and concepts in one file: /llms-full.txt. OpenAPI: /openapi.json.
POST/
Uploads a price table as a CSV file and saves it on a service in one request.
target_mode=existing_service: permanently overwrites the service's whole price table. Any existing options or price breaks missing from the file are deleted. The service's name and locations do not change.target_mode=create: creates a new service with these prices.
Send multipart/form-data. The import runs while you wait, and it is all or nothing. If anything in the file is wrong, you get 422 with the first problem found (a row problem names its line), and nothing is saved: no price changes and no import job. Otherwise the response is the finished import job, always completed with failed_item_count 0. You can read it again later with GET /v1/api/store/{team_slug}/imports/{hash_id}. There is never a file of failed rows for these imports.
Only service pricing is accepted here (entity_type=service_pricing). Customer, vendor and product CSV files are imported in the YoPrint web app's Import Portal.
The file. Start from PUT /v1/api/store/{team_slug}/csv/template/service_pricing.
- A
.csvfile of at most 6,240 KB, with at most 600 data rows and at least one, in UTF-8. - The first header must be exactly
qty/type. Add at least one more column; each is an option of the service (for example1 Color). Header names can't be blank or repeated. - Each row is a price break.
qty/typeis a number, the first row's must be 1, and no quantity can repeat. - Every cell needs a value. A cell is one price (
12.50) or two,first|additional(12.50|9.00). Prices are numbers with at most 3 decimal places, written with a digit before the point (0.50, not.50) and no spaces.
Business rules
entity_typemust beservice_pricing. Any other value is a 422.target_modeisexisting_serviceorcreate. Withexisting_service, sendservice_id. Withcreate, sendname,allow_cust_loc,pricing_typeand at least one entry inlocations.- Every location id must be one of your team's product locations, each listed once, or the request fails with "One or more service locations are invalid."
- The file must have the
.csvextension and be at most 6,240 KB. It must hold at least one and at most 600 data rows. - First header exactly
qty/type, then at least one option column. Option headers can't be blank or repeated. - Each
qty/typevalue is a number, the first data row's is 1, and quantities can't repeat. - No empty cells (
0is a value). Each cell is a price, orfirst|additional. Prices are numbers with at most 3 decimal places, between -2,147,483,648 and 2,147,483,648. Write a digit before the decimal point and no spaces:.50or12passes the number check but then fails with a server error. - All or nothing: the whole file is checked before anything is saved, and any problem stops the import with nothing saved. A returned job is always
completed, with one item andfailed_item_count0. - One price import per service at a time, and one per new service name. A second upload waits up to 45 seconds for the first to finish; if the first is still running then, it fails with a server error. Retry after the first finishes.
Side effects
existing_service: permanently overwrites the service's whole price table with the file. Options and price breaks missing from the file are deleted. The service's name and locations do not change.create: creates a service at the end of your service list, links the locations, then saves the prices.- If the service's pricing type is not
additional, the second price infirst|additionalcells is ignored and the service keeps no additional-location prices. - A successful import is recorded as an import job, with the uploaded file attached. A failed one leaves no job: it does not appear in the import jobs list.
- People with YoPrint open see the service and the import update live.
Path parameters
Your team slug, shown as Team ID next to your API key in Profile.
- Example
your-team
- Example
Request body
multipart/form-data, required.
Which service gets the prices?
Send one of these 2 bodies.
An existing service
Permanently overwrites the service's whole price table with the file; omitted options and breaks are deleted.
- Always
"service_pricing"
- Always
- Always
"existing_service"
- Always
The CSV file: a name ending in
.csv, at most 6,240 KB and 600 data rows.The service's id (a hash id), for example the
service_idof a job's service.
Example request
curl -X POST 'https://secure.yoprint.com/v1/api/store/your-team/imports/csv' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Accept: application/json' \ -H 'X-Yoprint-Decimal-Format: exact-v1' \ -H 'Content-Type: application/json' \ -d '{ "entity_type": "service_pricing", "file": "qty/type,1 Color,2 Color\n1,10.00|11.00,12.00|13.00\n12,8.50|9.00,10.00|11.00\n", "service_id": "IIVE3hadAUOmIA", "target_mode": "existing_service" }'A new service
Creates a service with this name, these locations and these prices.
- Always
"service_pricing"
- Always
- Always
"create"
- Always
"Allow custom locations on jobs":
1for yes,0for no. A form sends text, andtrueorfalseare refused.- One of:
"1","0"
- One of:
The CSV file: a name ending in
.csv, at most 6,240 KB and 600 data rows.The product locations the service applies to, at least one, each once. Send each id as its own form field with a numbered index:
locations[0][id],locations[1][id], and so on. A JSON text in a singlelocationsfield is refused (locationsmust be an array).- At least 1 item
Fields of each item (1)
A product location's
id(a hash id) fromGET /v1/api/store/{team_slug}/service_location.
The new service's name.
single: per-item pricing.additional: per-item pricing with a second price for each additional location.single_flat: a flat rate per job.- One of:
"single","additional","single_flat"
- One of:
Example request
curl -X POST 'https://secure.yoprint.com/v1/api/store/your-team/imports/csv' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Accept: application/json' \ -H 'X-Yoprint-Decimal-Format: exact-v1' \ -H 'Content-Type: application/json' \ -d '{ "allow_cust_loc": "1", "entity_type": "service_pricing", "file": "qty/type,1 Color,2 Color\n1,10.00|11.00,12.00|13.00\n12,8.50|9.00,10.00|11.00\n", "locations": [ { "id": "454DGJ3i6EQpq0" }, { "id": "XDz01LsIUDpguv" } ], "name": "Puff print", "pricing_type": "additional", "target_mode": "create" }'Response
200Successful response.
Child fields (20)
A status slug formatted for UI display: matches
status, or isimported_with_unresolved_rowswhen some items succeeded and some failed.What was imported. For CSV imports:
service_pricing(the CSV type supported via API), or older uploader types (customer,vendor,simple_product,variant_product,custom_product). For Printavo imports: Printavo record types (such ascustomer,vendor).Items that failed. Always 0 on service pricing imports: they are all or nothing, and any error fails with 422 without creating an import job. Only Printavo imports and retired CSV uploader jobs can have failed items.
The import job's id (a hash id).
Where the data came from.
- One of:
"csv","printavo"
- One of:
completed,completed_with_errorsandfailedare final.- One of:
"pending","staged","ready_to_commit","committing","completed","completed_with_errors","failed"
- One of:
Items saved.
Rows (items) in the job. A service pricing job has one item: the whole price table.
already_imported_countintegerItems skipped because they were imported before.
auto_commitbooleanTrue when the job saves its rows without a separate review step.
completed_atstring (date-time) | nullWhen the job reached a final status.
display_status_labelstring | nullA human-readable status label:
"Imported with unresolved rows"whendisplay_statusisimported_with_unresolved_rows, otherwise null.duplicate_countintegerItems skipped as duplicates within the job.
error_csv_urlstring | nullWeb app download URL for a failed-rows CSV. Null for service pricing and Printavo jobs, and for new imports. Only retired CSV uploader jobs (pre-September 2026) may have a link. Because this is a web app link that API keys cannot open, download the file via the API using
GET /v1/api/store/{team_slug}/imports/{hash_id}/artifacts/error-csv. Checkfailed_item_countto see if items failed.legacy_statusstring | nullAn older status word kept for the web app, such as
import_successorvalidation_failed. Preferstatus.source_account_refstring | nullThe connected source account, for Printavo jobs.
target_modestring | nullService pricing jobs:
existing_serviceorcreate. Otherwise usually null.unresolved_item_countintegerFailed items: the same number as
failed_item_count.updated_atstring (date-time) | null
Errors
401The API key is missing or wrong (
unauthenticated).403You are not allowed to do this.
api_access_not_in_plan: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only{"message": "Insufficient Permission"}. Other refusals use the error envelope, withunauthorizedoruser_is_not_authorized.- When
- You lack the service permissions the target mode needs (a plain
{"message": "Insufficient Permission"}body, not the usual envelope). Checked early, beforetarget_mode, the service and the file's contents; without a validtarget_modeit asks for both create and update. - Fix
- Ask a team admin for permission to create or update services.
404Not found.
page_not_found: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's.code: null, with a message that starts "No query results for": there is no record with that id.- When
service_idmatches no service of this team.- Fix
- Check the service's id.
422The request was refused and changed nothing.
validation_failedlists what to fix inerror.fields; other codes name a business rule, explained inerror.message. A few checks answer with only{"message": "..."}. An operation that calls another service (a payment provider, a carrier, an accounting app) can answerconnect_timeout("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.- When
- A field is missing or invalid, the file is not
.csvor is over 6,240 KB, or a location id is not yours (validation_failed;error.fieldsnames the field). - Fix
- Fix the field and send again.
- When
- The file breaks a rule:
error.codesingle_error, the problem inerror.message, anderror.detail, a list holding the same text. A row problem starts with its line, as in "[LINE 3] Duplicate Quantity Break Detected: 12"; header, encoding and empty-file problems have no line. - Fix
- Fix the file and upload it again. Nothing was saved.
- When
- The file has more than 600 data rows: a plain
{"message": "This import expands to 601 source rows. The limit for service_pricing imports is 600 source rows per request."}body, not the usual envelope. - Fix
- Split the table, or remove unused price breaks.
500A business rule refused the request (see the operation's errors), or something failed inside YoPrint. Refusals use the error envelope with a code such as
action_not_allowed; an unexpected failure answers{"message": "Server Error"}. Repeating a refusal gets the same answer; after an unexpected failure, read the record back before you retry a change.- When
- A price or quantity is written like
.50or with a leading space, or a second upload for the same service (or the same new service name) waited 45 seconds and the first was still running ({"message": "Server Error"}). Nothing was saved. - Fix
- Write numbers like
0.50with no spaces; for the second case, retry after the first upload finishes.
Notes
- Re-uploading the same file to the same service is safe: the result is the same table. With
create, each successful upload makes another service, even with the same name. - Line numbers in error messages count the header as line 1, so the first data row is line 2.
- Prices are saved as given, with up to 3 decimal places.
- The public API has no operation that lists services. A job's services carry their
service_id; take it from there or from YoPrint. Product location ids come fromGET /v1/api/store/{team_slug}/service_location.
Related
- PUTDownload the service pricing CSV template
/v1/ api/ store/ {team_slug}/ csv/ template/ service_pricing - GETList service locations
/v1/ api/ store/ {team_slug}/ service_location - GETGet an import job
/v1/ api/ store/ {team_slug}/ imports/ {hash_id}
