Inbound Endpoints & Integration Tasks

How inbound loads work: submit records, follow the integration task to completion, cancel, delete and review.

Inbound endpoints are how you send master and transactional data to GAINS. This page explains what happens to a batch after you send it: the responses a submit can return, the integration task that tracks the batch while GAINS loads it, and how to follow, cancel or delete what you sent.

How an inbound submit works

  1. You POST a batch of records to an inbound endpoint, for example POST /locations.
  2. GAINS Connect checks each record against the endpoint's rules: required fields, lengths, formats and value ranges. Each record is accepted or rejected on its own.
  3. If at least one record is accepted, GAINS Connect creates an integration task for the accepted records and answers at once with its TaskId. Records rejected at this point are listed in the same response and are not part of the task.
  4. GAINS loads the task's records at its next scheduled load for that kind of data. The task moves to Processing, then to a final status that gives the record counts and lists any record GAINS refused while loading it.

A 202 or 207 response therefore means the records were accepted for loading, not that they are already in GAINS. Follow the task to know when they are.

Sending a batch

Every inbound endpoint takes a JSON array of records. A request may hold at most 1000 records. The limit counts the top-level records only: on endpoints whose records carry nested data, such as orders with LineItems, a request can hold 1000 orders, each with its own lines.

POST /api/v1/locations HTTP/1.1
Host: acme.gainsystems.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Idempotency-Key: 6f1c2b8e-3d4a-4f5b-9c7e-0a1b2c3d4e5f
[
  {
    "LocationCode": "CHI01",
    "Name": "Chicago Distribution Center",
    "Type": "DC",
    "City": "Chicago",
    "State": "IL",
    "Country": "US",
    "PostalCode": "60607",
    "CurrencyCode": "USD"
  },
  {
    "LocationCode": "MKE02",
    "Name": "Milwaukee Branch",
    "Type": "Branch",
    "ParentDefaultCode": "CHI01",
    "City": "Milwaukee",
    "State": "WI",
    "Country": "US",
    "PostalCode": "53202",
    "CurrencyCode": "USD"
  }
]

When every record is accepted, GAINS Connect answers 202 with the task:

{
  "Status": "Queued",
  "TaskId": "c2791b62-508f-4aaa-b011-3764ff4625ee"
}

Status is Queued when the next GAINS load for this data is scheduled, and Received when it is not yet scheduled. A SKU-location delete answers PendingReview instead (see SKU-location delete review).

Keep the TaskId: it is how you follow the batch from here on.

Submit responses

StatusMeaningWhat to do
202 AcceptedEvery record was accepted. The body holds Status and TaskId.Follow the task.
207 Multi-StatusSome records were accepted and a task was created for them. The body also lists the rejected records under RejectedRecords.Follow the task, and correct and resend only the rejected records.
422 Unprocessable ContentNothing was accepted and no task was created.Correct the request and send it again.
429 Too Many RequestsGAINS Connect is still taking in earlier batches and did not accept this one. Nothing was saved.Wait for the number of seconds in the Retry-After header, then send the same batch again.
400 Bad RequestThe request could not be read: malformed JSON, a field name the endpoint does not know, a value of the wrong JSON type, or a reused Idempotency-Key. Nothing was saved.Correct the request.
401 / 403The credentials are missing or rejected, or lack access to this endpoint.See Authentication.
🚧

A 207 is a partial success

Treat 207 as "accepted, with exceptions", not as a failure. The accepted records are already in a task and will be loaded, so resending the whole batch would submit them twice.

A 207 carries the task's Status and TaskId and, under RejectedRecords, each rejected record with its reasons; see Rejections at submit.

A 422 comes in one of two forms. When every record failed its checks, the body has the same RejectedRecords list, with Status set to RejectedFailedProcessing and no TaskId. When the request itself is refused, for example because the array is empty or holds more than 1000 records, the body is a single Reason:

{
  "Reason": "The data provided exceeds the maximum constraint. Please submit a maximum of 1000 records."
}

How to read and act on RejectedRecords is described in Rejected Records.

Integration tasks

An integration task tracks one accepted batch from the moment GAINS Connect accepts it until GAINS has finished loading it. Each task has a TaskType naming the kind of data it carries, for example InboundLocation or DeleteSku (see Task types).

Task statuses

StatusMeaningFinal
ReceivedAccepted. No GAINS load for this data is scheduled yet.No
QueuedAccepted and waiting for the next scheduled GAINS load. StatusMessage gives the expected processing time.No
PendingReviewA SKU-location delete held until you approve or reject it. Nothing has been deleted.No
ProcessingGAINS is loading the records.No
CompletedLoading finished and at least one record was loaded. Batch gives the counts and lists any record GAINS refused.Yes
RejectedFailedProcessingLoading finished but GAINS refused every record. Batch.RejectedRecords says why.Yes
RejectedNotApprovedYou rejected a SKU-location delete under review. Nothing was deleted.Yes
CancelledYou or GAINS cancelled the task before it was processed. Nothing was loaded.Yes
FailedThe load stopped on an unexpected error. Send the batch again, and contact your GAINS representative if it fails again.Yes

Reading a task

Call GET /tasks/{TaskId} with the TaskId from the submit response:

{
  "TaskId": "c2791b62-508f-4aaa-b011-3764ff4625ee",
  "TaskType": "InboundLocation",
  "Status": "Queued",
  "StatusMessage": "Expected processing time is 2026-05-22 02:00:00.",
  "Batch": {
    "TotalRecords": 2,
    "Accepted": null,
    "AcceptedWithExclusions": null,
    "Rejected": null,
    "RejectedRecords": []
  },
  "StatusUpdatedOn": "2026-05-21 16:12:08",
  "CreatedOn": "2026-05-21 16:12:08",
  "CreatedBy": "[email protected]",
  "UpdatedBy": null,
  "Comments": null,
  "UpdatedOn": null
}
FieldDescription
TaskIdThe task's identifier, as returned by the submit.
TaskTypeThe kind of data the task carries.
StatusThe current status (see Task statuses).
StatusMessageA readable explanation of the status, such as the expected processing time while Queued.
Batch.TotalRecordsThe number of records in the task. Records rejected at submit are not counted.
Batch.Accepted, Batch.AcceptedWithExclusions, Batch.RejectedThe load counts. null until the task is Completed or RejectedFailedProcessing.
Batch.RejectedRecordsThe records GAINS refused while loading them, each with its Reasons. Empty until the task is final; see Rejections while loading.
StatusUpdatedOnWhen the task entered its current status.
CreatedOn, CreatedByWhen the task was created, and the user or client that submitted it.
UpdatedBy, UpdatedOnWho approved or declined a reviewed task, and when.
CommentsThe note recorded with a review decision, if any.

Times are GAINS server time, in the form YYYY-MM-DD HH:MM:SS.

Following a task

You can follow tasks in two ways:

  • Polling. Call GET /tasks/{TaskId} until Status is final. A task does not move until the GAINS load runs, so while it is Queued, use the expected processing time in StatusMessage to decide when to check again.
  • Webhook. Enable the TaskStatusUpdate trigger. Each time a task's status changes you receive a delivery whose Data list holds the task, in the same form as above; one delivery can carry several tasks. Add a filter such as Status IN ('Completed', 'RejectedFailedProcessing', 'RejectedNotApproved', 'Cancelled', 'Failed') to receive final statuses only. See Webhooks.

Tasks are kept for 30 days after they are created.

Listing tasks

GET /tasks returns tasks oldest first, in pages, under Data, as described in Pagination.

ParameterDescription
StatusOne status, or several separated by commas: Completed,RejectedFailedProcessing.
TaskTypeOne task type, for example InboundSku.
DateStart, DateEndTasks created on or after, and on or before, this time. Send a date and time: 2026-05-21 00:00:00.
Size, CursorPaging, as above.
GET /api/v1/tasks?Status=RejectedFailedProcessing,Failed&DateStart=2026-05-21%2000:00:00 HTTP/1.1
Host: acme.gainsystems.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Cancelling a task

POST /tasks/{TaskId}/cancel withdraws a task that GAINS has not started loading: one in Received, Queued or PendingReview. Once a task is Processing it can no longer be cancelled, and the request answers 400.

{
  "CancelledOn": "2026-05-21 16:20:44",
  "Status": "Cancelled"
}

Deleting records

Most kinds of master and transactional data can also be deleted, through an endpoint ending in /delete, for example POST /skus/delete. The Task types table lists which subjects have one. A delete takes the keys of the records, answers like a load and creates a task you follow the same way. Endpoints, keys and rules are in Deleting Data & SKU-Location Review.

POST /skus-locations/delete deletes nothing until you approve it: its task starts in PendingReview. See SKU-location delete review.

Task types

TaskType identifies the endpoint a task came from. Use these values in the TaskType filter of GET /tasks.

EndpointTaskTypeDelete endpointTaskType
POST /skusInboundSkuPOST /skus/deleteDeleteSku
POST /locationsInboundLocationPOST /locations/deleteDeleteLocation
POST /skus-locationsInboundSkuLocationPOST /skus-locations/deleteDeleteSkuLocation
POST /customersInboundCustomerPOST /customers/deleteDeleteCustomer
POST /vendorsInboundVendorPOST /vendors/deleteDeleteVendor
POST /vendor-alternativesInboundVendorAlternativePOST /vendor-alternatives/deleteDeleteVendorAlternative
POST /bills-of-materialInboundBOMPOST /bills-of-material/deleteDeleteBOM
POST /supercessionsInboundSupercessionPOST /supercessions/deleteDeleteSupercession
POST /price-breaksInboundPriceBreakPOST /price-breaks/deleteDeletePriceBreak
POST /demand-transactions, POST /demand-transactions-splitsInboundDemandTransPOST /demand-transactions/deleteDeleteDemandTrans
POST /demand-eventsInboundDemandEventPOST /demand-events/deleteDeleteDemandEvent
POST /supply-transactions, POST /supply-transactions-splitsInboundSupplyTransPOST /supply-transactions/deleteDeleteSupplyTrans
POST /usage-transactionsInboundUsageTransPOST /usage-transactions/deleteDeleteUsageTrans
POST /forecastsInboundImportedForecastPOST /forecasts/deleteDeleteImportedForecast
POST /inventory-lotsInboundOnHandLots
POST /inventory-availabilityInboundInventoryPolicyAvailability
POST /skul-alternativesInboundAlternativeSkuls
POST /work-order-componentsInboundWorkOrderComponent
POST /vendor-constraintsInboundVendorConstraint
POST /partners/vendor-parametersInboundPartnerVendorParams
POST /calendar/daily, /weekly, /monthlyInboundCalendarOverride
POST /lia-input/weekly, /monthlyInboundLeadingIndicatorAnalysis
POST /custom/{ApiRoute}The task type set up for that custom schema. Filter with InboundCustomSchema.

POST /supply-transactions/link-order is the one inbound endpoint that creates no task: it takes a single object rather than an array and answers 202 with no body.

Retrying safely

After a 207, resend only the corrected RejectedRecords. After a 429 or a timeout, resend the same batch with the same Idempotency-Key. See Decide whether to retry and Load Throughput & Retries.

Related guides


What’s Next

Did this page help you?