Skip to main content

Create a product

POSThttps://api.fieldtask.example/public/v1/products
Permissions
products:create, products:read
Safe retries
Idempotency-Key
Also available as
fieldtask_create_product

Creates one catalog product or service, with 1–20 one-time prices, in the organization your credential is connected to.

The product and all of its prices are saved together or not at all. The new product is ready to use straight away: it appears in List products, and you can add it to jobs with its price IDs.

This endpoint only creates. It never matches or updates an existing product, and it doesn't check whether a product with the same name already exists. If you send the same body twice without the same Idempotency-Key, you get two products. Search with GET /public/v1/products?search=… first if you're not sure. See Idempotency and safe retries.

Preview

This page is generated from contract 2026-11-03-review.2, a review export that hasn't been released. Product creation works only where FieldTask has enabled it.

Before you start​

You need:

  1. API access for your organization. An owner or admin requests it in Settings → API connections. Until FieldTask enables it, every request returns 403 api_access_not_enabled.
  2. A REST credential with the permissions below: an API token created in Settings → API connections, or an access token from an OAuth client-credentials connection. Tokens from an MCP sign-in don't work with the REST API.

No other records are needed. Prices are created with the product, so you don't send any IDs.

Permissions​

Every request needs all of these permissions:

PermissionDependency in the permission map
products:createDepends on products:read
products:readNo dependencies

No inputs need additional permissions.

Adding a permission to a connection doesn't happen automatically. An owner or admin edits it in Settings → API connections. API tokens keep the same token string when their permissions change. OAuth integrations must also request the new scope when they get an access token.

Request​

Responses​

Created

Response Headers
    Cache-Control

    Responses must not be cached.

    Idempotency-Key

    The operation key used for this create, including a server-generated key when omitted.

    Idempotency-Replayed
    Location
    Operation-Location
    X-Request-ID

    Server-generated request correlation ID.

Field guide​

The request body is a single JSON object. name, item_type and prices are required.

FieldRequiredWhat to send
nameYesThe product or service name, 1–256 characters. It can't be only spaces. Spaces at either end are removed before saving.
item_typeYesproduct, service or other.
pricesYes1–20 price options. See Prices.
descriptionNoUp to 4,096 characters, saved exactly as sent. Omit it to leave the description empty (null); sending null is rejected.
is_availableNotrue or false. Defaults to true. Products with is_available: false stay in the catalog but can't be added to jobs through the API.

Anything else in the body is rejected with 422 validation_failed, including fields such as id, unit_price, organization_id, images or stock levels. The organization always comes from your credential.

Prices​

Each entry in prices becomes one named price option with its own ID.

Price fieldRequiredWhat to send
nameYesThe option's label, such as Standard or After hours, 1–256 characters. It can't be only spaces. Spaces at either end are removed.
amountYesA decimal string. See Amounts and currency.
currencyNoThree uppercase letters, such as USD. Defaults to your organization's currency.
price_typeNoOnly one_time is accepted, and it's the default. You can't create recurring prices.
  • Give each option a name your users will recognize. When a product has more than one price, anyone adding it to a job has to choose one by ID, and an AI client asks the user to choose by name.
  • You can't add, change or remove prices later through the API. Plan the full set of options in this request.

Amounts and currency​

amount is a string, not a JSON number, so amounts are never rounded in transit.

  • Up to 13 digits before the decimal point and up to 2 after: "0", "185", "185.5" and "185.50" are all valid.
  • No sign, no exponent, no thousands separators and no leading zeros: "-5", "1e3", "1,250.00" and "0185" are rejected.
  • "0" is accepted and creates a zero-price option.
  • Responses always show two decimal places: "185" comes back as "185.00".

FieldTask stores the currency you send, or your organization's currency if you omit it, and never converts amounts. Only prices in your organization's currency can be added to jobs through the API, so leave currency out unless you have a reason to set it.

Body and header rules​

  • Send Content-Type: application/json with an uncompressed body. A Content-Encoding header is rejected with 415.
  • The body can be at most 256 KiB and nest at most 10 levels deep.
  • Duplicate property names, NaN and Infinity are rejected with 400 invalid_json.
  • The URL must have no query string and no trailing slash.

The created product​

The 201 response returns the product as Get a product would:

  • prices lists every option with its new id, source: "price" and currency_source: "price". Options are listed newest first, so they can come back in a different order from your request. Match them by name.
  • Products created through the API have no legacy unit price, so there's no source: "legacy" option.
  • The product is approved in your catalog immediately. Products that reach FieldTask in other ways may need approval before the API shows them.

To add it to a job, use Add a job item or the items field of Create a job with the product's id and the id of the price to charge.

Idempotency and safe retries​

Send an Idempotency-Key header with every create, and reuse the same key and body when you retry after a timeout, a connection error, a 5xx, 409 operation_in_progress or 429. A replay within seven days returns the original product with Idempotency-Replayed: true and creates nothing. A key is tied to this kind of request, so the same key on Create a job is a separate request.

The full rules, including key format, scoping and the seven-day window, are in Idempotency and recovery.

If the product has since been deleted or is no longer approved in the catalog, replaying its key returns 410 resource_gone instead of the original result.

Local creation and external synchronization​

A 201 response means the product and its prices are saved in FieldTask and can be used straight away. Nothing further is needed, and later follow-up work never removes them.

If your organization has a connected CRM set up to receive products created in FieldTask, the product is also queued for external synchronization. That work runs separately and is reported on the operation, not on the product. Read the Operation-Location URL from the 201 response with the same credential (needs products:read):

curl "$FIELDTASK_API_ORIGIN/public/v1/operations/OPERATION_ID" \
--header "Authorization: Bearer $FIELDTASK_API_TOKEN"
  • Without a connected CRM receiving products, data.integration.status is not_applicable.
  • With one, the operation has a target for the product. Once the product has been sent, a further target is added for each price, so integration.targets can grow between reads.
  • Synchronization is attempted once. If its outcome can't be confirmed, the target reports outcome_unknown with reconciliation_required and isn't retried automatically. Check the connected CRM before doing anything else; creating the product again in FieldTask creates a second product.

See Get an operation for every status and error code.

Pending confirmation

In the review contract, FieldTask hasn't enabled product synchronization. If your organization's connected CRM is set to receive products, the operation reports a blocked target with configuration_required. The product itself is still created.

Operation results are kept for seven days. After that, GET /public/v1/operations/{id} returns 410 operation_result_expired. That doesn't mean the product wasn't created.

Recovering from failures​

What happenedWhat to do
Timeout, connection error or 5xxRetry the same body with the same key, with exponential backoff.
409 operation_in_progress or 429Wait for Retry-After seconds and retry with the same key.
201 receivedStore data.id, each price id, and the Location and Operation-Location headers. Stop retrying.
Key older than 7 days, or the result expiredDon't create again yet. Search with GET /public/v1/products?search=… for a product with the same name.

Never switch to a new key just because a response was lost. A new key means a new product.

Errors​

Errors use application/problem+json. The top-level code is the stable machine-readable value. For a missing permission, code is insufficient_scope and error.code is missing_permission. Validation errors don't say which field failed, and error responses never echo submitted values. See Errors.

StatuscodeMeaningWhat to do
400invalid_queryThe URL has a query string. Create requests accept none.Send the request to /public/v1/products with no query parameters.
400invalid_jsonThe body isn't one valid JSON value, repeats a property name, uses NaN/Infinity or nests deeper than 10 levels.Send a single JSON object encoded as UTF-8.
400invalid_operation_keyIdempotency-Key isn't 16–128 printable ASCII characters.Use a UUID or another 16–128 character key without spaces at either end.
401invalid_tokenThe bearer token is missing, malformed, expired, revoked or issued for a different resource (for example an MCP sign-in).Send a current REST API token or REST OAuth access token as Authorization: Bearer ….
403api_access_not_enabledFieldTask hasn't enabled API access for this organization, or has disabled it.An owner or admin requests access in Settings → API connections.
403insufficient_scopeThe connection lacks products:create or products:read. error.code is missing_permission.Add the permission named in error.message to the connection, then retry with the same key.
403operation_forbiddenThe connection's access changed while the request was processed, or the result would include data the connection can no longer read.Check the connection's permissions, then retry. Retry creates with the same key and body.
404resource_not_foundProduct creation isn't available in this environment, or the URL is wrong.Check the API origin and path (no trailing slash).
405method_not_allowedProduct creation isn't enabled in this environment. The Allow header lists the methods that are.Contact FieldTask if you expected product creation to be available.
409idempotency_key_reusedThis key was already used to create a product with a different body.Resend the original body with this key, or use a new key for a different product.
409operation_in_progressA request with the same key is still being processed.Wait for Retry-After seconds, then retry with the same key and body.
409idempotency_window_expiredThis key was used more than seven days ago. It can't be replayed or reused. This doesn't mean the original request failed.Search with GET /public/v1/products?search=… before creating anything new.
410resource_goneThis key created a product that has since been deleted or is no longer approved in the catalog.Don't create it again automatically. Check in FieldTask whether it was removed on purpose.
413payload_too_largeThe body is larger than 256 KiB.Shorten the description or price names.
415unsupported_media_typeContent-Type isn't application/json, or the body is compressed.Send an uncompressed body with Content-Type: application/json.
422validation_failedA required field is missing; a field is unknown; a name is empty or only spaces; item_type isn't product, service or other; prices has fewer than 1 or more than 20 entries; or an amount, currency or price_type isn't valid.Check the request against the field guide. The response doesn't name the field.
429rate_limit_exceededToo many requests in the current minute, or too many concurrent requests, for the organization or connection.Wait for Retry-After seconds, then retry.
429daily_quota_exceededThe organization or connection has used its daily allowance for this kind of request. Allowances reset at 00:00 UTC.Wait for Retry-After seconds, or check remaining allowance in Settings → API connections.
503temporarily_unavailableThe service couldn't complete the request. The product may or may not have been created.Retry with the same key and body, with exponential backoff.

Every response, including errors, has an X-Request-ID header. Include it, with the time and the status code, when you contact support. Never send tokens or secrets.

Limits​

Product creates count against the same per-minute, concurrency and daily create allowances as other creates. A replay with the original key doesn't use another create. See Rate limits and quotas.

Response headers​

A successful create returns 201 Created with these headers:

HeaderAlways presentValue
Cache-ControlYesAlways no-store
Idempotency-KeyYesstring
Idempotency-ReplayedYesstring
LocationYesstring
Operation-LocationYesstring
X-Request-IDYesuuid

Location is the product's path, /public/v1/products/{id}. The server also returns the Idempotency-Key that was used, including one it generated.