Skip to main content
https://.thoughtindustries.com

Bulk Update Assignment Submission License

Sets or clears the license associated with many assignment submissions in a single request.

Sets or clears the license associated with many assignment submissions in a single request.

POSThttps://example.thoughtindustries.com/v3/assignmentSubmissions/bulkUpdateLicense

This endpoint is served by the v3 API. Its full path is https://{instance}.thoughtindustries.com/v3/assignmentSubmissions/bulkUpdateLicense, not the /incoming/v2 base URL used by the rest of this reference. Use the full URL shown in the examples below.

Example request — set a license per submission

curl -X POST "https://{instance}.thoughtindustries.com/v3/assignmentSubmissions/bulkUpdateLicense" \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "submissions": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "licenseId": "223e4567-e89b-12d3-a456-426614174111"
    },
    {
      "id": "323e4567-e89b-12d3-a456-426614174222",
      "licenseId": "423e4567-e89b-12d3-a456-426614174333"
    }
  ]
}'

Example request — apply one license to many submissions

curl -X POST "https://{instance}.thoughtindustries.com/v3/assignmentSubmissions/bulkUpdateLicense" \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "ids": [
    "123e4567-e89b-12d3-a456-426614174000",
    "323e4567-e89b-12d3-a456-426614174222"
  ],
  "licenseId": "223e4567-e89b-12d3-a456-426614174111"
}'

Example request — clear the license

Pass licenseId: null to remove the license from a submission. This works in either shape.

curl -X POST "https://{instance}.thoughtindustries.com/v3/assignmentSubmissions/bulkUpdateLicense" \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "ids": ["123e4567-e89b-12d3-a456-426614174000"],
  "licenseId": null
}'

Parameters

Requires the assignments.grade permission. A standard company API key satisfies this.

Send submissions, or ids with a sibling licenseId, or both. At least one of the two lists must be present and non-empty.

NameTypeRequiredLocationDescription
submissionsBulkUpdateSubmissionItem[]One of submissions / idsbodyPer-submission updates, each naming its own target license.
idsuuid[]One of submissions / idsbodySubmission IDs that all receive the same licenseId. Requires the sibling licenseId field.
licenseIduuid | nullRequired when ids is presentbodyLicense applied to every entry in ids. Pass null to clear. Ignored when ids is absent.

Each object in submissions (BulkUpdateSubmissionItem):

FieldTypeRequiredDescription
iduuidYesAssignment submission to update.
licenseIduuid | nullYesTarget license for this submission. Pass null to clear the current license.

Batch limit: 1000 submissions per request. The Batch limit exceeded error counts unique submission IDs after submissions and ids are merged — not each array separately. A request with 600 IDs in each list but only 900 distinct IDs between them is accepted. Each array is also capped at 1000 entries on its own, and that cap is enforced earlier, during request validation, so a single array of 1001 entries returns the opaque 400 described under Errors instead of the friendly message.

Precedence: ids wins. When the same submission ID appears in both submissions and ids, the shared licenseId from ids is applied and the per-item value is discarded.

Only submissions and licenses belonging to your instance can be referenced. IDs from another instance are treated as not found.

Example response

Both shapes return 200 with the same body. Note the data wrapper — v3 endpoints nest their payload under data.

{
  "data": {
    "updatedRecordCount": 2,
    "errors": []
  }
}

Response fields

FieldTypeDescription
data.updatedRecordCountintegerNumber of submissions whose license was actually changed.
data.errorsobject[]Per-item failures. Empty when every submission was updated.
data.errors[].iduuidThe ID that triggered the failure — a submission ID, or a license ID when record is license.
data.errors[].errorstringHuman-readable reason the entry failed.
data.errors[].recordstringWhich side of the update failed: submission or license.

Partial success

Individual failures never fail the batch. Unknown submissions, unknown licenses, and IDs owned by another instance are collected into errors[] and skipped; everything else still updates. A response with a non-empty errors[] and a 200 status is normal — always read updatedRecordCount rather than relying on the status code alone.

An unrecognized submission ID returns:

{
  "data": {
    "updatedRecordCount": 0,
    "errors": [
      {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "error": "Submission not found or does not belong to company",
        "record": "submission"
      }
    ]
  }
}

Two behaviors are worth planning for:

  • An unrecognized license produces one error entry per affected submission, and each entry carries the license ID rather than the submission ID. Ten submissions pointing at the same bad license yield ten identical entries. Use record: "license" to tell these apart from submission failures.
  • Duplicate submission IDs are reported but not skipped. A duplicate adds an entry with the message Duplicate submission id in payload — last value applied, and the last value still applies, so updatedRecordCount can be positive alongside a non-empty errors[].

Updates are surgical: only the submission's license and its updated-at timestamp change. Grades, feedback, and submission content are untouched.

Search indexing for updated submissions happens asynchronously after the response is sent. It never affects the result, but the search index is eventually consistent — a submission may briefly appear under its previous license in search results.

Errors

Three conditions fail the entire request with a message naming the problem. All return 400 with code: "VALIDATION_ERROR":

ConditionMessage
Neither submissions nor ids suppliedAt least one of `submissions` or `ids` must be provided
ids supplied without a sibling licenseId`licenseId` is required when `ids` is provided (may be null to clear)
More than 1000 unique IDs after merging, each array being within 1000Batch limit exceeded: combined item count must be <= 1000
{
  "code": "VALIDATION_ERROR",
  "message": "At least one of `submissions` or `ids` must be provided",
  "category": "VALIDATION",
  "timestamp": "2026-08-12T00:00:00.000Z",
  "statusCode": 400,
  "metadata": {
    "errors": ["submissions", "ids"]
  }
}

A missing or invalid API key returns 401 with code: "UNAUTHENTICATED", and a key without the assignments.grade permission returns 403 with code: "FORBIDDEN". Both use the same body shape with category: "CLIENT_ERROR" and carry no metadata.

Malformed field values return an opaque 400. A value that is not a valid UUID, any property the endpoint does not recognize, or more than 1000 entries in a single array is rejected before the request reaches the endpoint. All three return code: "BAD_REQUEST", category: "CLIENT_ERROR" and message: "Bad Request Exception", with no indication of which field was at fault. Validate UUIDs, check field spelling, and split oversized arrays client-side; log the request body alongside the response.

Rate limit

500 requests per 60 seconds, counted per instance. This limit is specific to this endpoint and is independent of the per-family rate limits that apply to /incoming/v2 endpoints.