MENU navbar-image

Introduction

Certification Bodies API

External API for Approved Certification Bodies to manage product certification.

ID Format

ZDHC Sandbox uses a structured ID format: {prefix}-XXXXXXX-Z

Prefix Entity Type Example
01 Organisation 01-XXXXXXX-Z
20 Product 20-XXXXXXX-Z
40 InCheck Report 40-XXXXXXX-Z

Error Model

All non-2xx responses use a consistent error structure:

{
    "error": {
        "code": "string",
        "message": "string",
        "details": {}
    }
}

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Retrieve a token by calling the /auth/login endpoint.

Certification Bodies/V1

CTZ Standard

List CTZ standards

requires authentication

Returns a paginated list of CTZ standards for this certification body.

Example request:
curl --request GET \
    --get "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/ctz-standard?per_page=1&page=1&filter%5Bname%5D=vero&filter%5Bversion%5D=1&filter%5Bctz_level_id%5D=11&filter%5Bctz_level_name%5D=est&filter%5Bstatus%5D=FAILED&filter%5Bstart_date%5D=%3C+2026-05-11+10%3A15%3A45&filter%5Bend_date%5D=%3C+2026-04-30+10%3A15%3A45&filter%5Bcreated_at%5D=%3E%3D+2026-05-03+10%3A15%3A45&filter%5Bupdated_at%5D=%3C+2026-05-15+10%3A15%3A45&search=qui&sort=name&column=name" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/ctz-standard"
);

const params = {
    "per_page": "1",
    "page": "1",
    "filter[name]": "vero",
    "filter[version]": "1",
    "filter[ctz_level_id]": "11",
    "filter[ctz_level_name]": "est",
    "filter[status]": "FAILED",
    "filter[start_date]": "< 2026-05-11 10:15:45",
    "filter[end_date]": "< 2026-04-30 10:15:45",
    "filter[created_at]": ">= 2026-05-03 10:15:45",
    "filter[updated_at]": "< 2026-05-15 10:15:45",
    "search": "qui",
    "sort": "name",
    "column": "name",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": 47,
            "name": "Foundational",
            "version": "3.1",
            "start_date": "2026-01-01T00:00:00.000000Z",
            "end_date": null,
            "status": "PASSED",
            "expired_at": null,
            "ctz_level_id": 1,
            "ctz_level_name": "Foundational"
        },
        {
            "id": 47,
            "name": "Foundational",
            "version": "3.1",
            "start_date": "2026-01-01T00:00:00.000000Z",
            "end_date": null,
            "status": "PASSED",
            "expired_at": null,
            "ctz_level_id": 1,
            "ctz_level_name": "Foundational"
        }
    ],
    "links": {
        "first": "/?page=1",
        "last": "/?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "active": false
            },
            {
                "url": "/?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "active": false
            }
        ],
        "path": "/",
        "per_page": 100,
        "to": 2,
        "total": 2
    }
}
 

Request      

GET api/certification-bodies/v1/{organisation_id}/ctz-standard

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

Query Parameters

per_page   integer  optional    

Number of items to show per page. Defaults to 100 Example: 1

page   integer  optional    

the page number to show. Example: 1

filter[name]   string  optional    

Filter column name by any accepted value. Filter by CTZ standard name Example: vero

filter[version]   integer  optional    

Filter column version by any accepted value. Filter by CTZ standard version Example: 1

filter[ctz_level_id]   integer  optional    

Filter column ctz_level_id by any accepted value. Filter by CTZ level ID Example: 11

filter[ctz_level_name]   string  optional    

Filter column ctz_level_name by any accepted value. Filter by CTZ level name Example: est

filter[status]   string  optional    

Filter column status by any accepted value. Matches exact value. Example: FAILED

Must be one of:
  • FAILED
  • PASSED
filter[start_date]   datetime  optional    

Filter column start_date by any accepted value. Filter by start date of CTZ standard validity period Example: < 2026-05-11 10:15:45

filter[end_date]   datetime  optional    

Filter column end_date by any accepted value. Filter by end date of CTZ standard validity period Example: < 2026-04-30 10:15:45

filter[created_at]   datetime  optional    

Filter column created_at by any accepted value. Filter by CTZ standard created date Example: >= 2026-05-03 10:15:45

filter[updated_at]   datetime  optional    

Filter column updated_at by any accepted value. Filter by CTZ standard updated date Example: < 2026-05-15 10:15:45

search   string  optional    

Search in all of these columns: name, version, ctz_level_name Example: qui

sort   string  optional    

sort by any accepted column: name, version, status, ctz_level_id, ctz_level_name, start_date, end_date, created_at, updated_at. prefix a "-" before the column name to sort in descending order Example: name

column   string  optional    

get a distinct list of filterable values for any of the columns: name, ctz_level_id, ctz_level_name, version, status, start_date, end_date, created_at, updated_at. Example: name

MRSL Standard

List MRSL standards

requires authentication

Returns a paginated list of MRSL standards for this certification body.

Example request:
curl --request GET \
    --get "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/mrsl-standard?per_page=11&page=1&filter%5Bname%5D=dolores&filter%5Bversion%5D=8&filter%5Bconformance_level_id%5D=6&filter%5Bconformance_level_name%5D=ratione&filter%5Bstatus%5D=FAILED&filter%5Bstart_date%5D=%3E%3D+2026-05-17+10%3A15%3A45&filter%5Bend_date%5D=%3C%3D+2026-04-29+10%3A15%3A45&filter%5Bcreated_at%5D=%3D+2026-05-19+10%3A15%3A45&filter%5Bupdated_at%5D=%3C+2026-05-04+10%3A15%3A45&search=error&sort=name&column=name" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/mrsl-standard"
);

const params = {
    "per_page": "11",
    "page": "1",
    "filter[name]": "dolores",
    "filter[version]": "8",
    "filter[conformance_level_id]": "6",
    "filter[conformance_level_name]": "ratione",
    "filter[status]": "FAILED",
    "filter[start_date]": ">= 2026-05-17 10:15:45",
    "filter[end_date]": "<= 2026-04-29 10:15:45",
    "filter[created_at]": "= 2026-05-19 10:15:45",
    "filter[updated_at]": "< 2026-05-04 10:15:45",
    "search": "error",
    "sort": "name",
    "column": "name",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": 9,
            "reference_id": "30-7V7GKLDBKXQA-I",
            "name": "Level 1",
            "version": "3.1",
            "start_date": "2025-12-01T00:00:00.000000Z",
            "end_date": "2027-12-31T00:00:00.000000Z",
            "status": "PASSED",
            "expired_at": null,
            "conformance_level_id": 1,
            "conformance_level_name": "Level 1"
        },
        {
            "id": 9,
            "reference_id": "30-7V7GKLDBKXQA-I",
            "name": "Level 1",
            "version": "3.1",
            "start_date": "2025-12-01T00:00:00.000000Z",
            "end_date": "2027-12-31T00:00:00.000000Z",
            "status": "PASSED",
            "expired_at": null,
            "conformance_level_id": 1,
            "conformance_level_name": "Level 1"
        }
    ],
    "links": {
        "first": "/?page=1",
        "last": "/?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "active": false
            },
            {
                "url": "/?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "active": false
            }
        ],
        "path": "/",
        "per_page": 100,
        "to": 2,
        "total": 2
    }
}
 

Request      

GET api/certification-bodies/v1/{organisation_id}/mrsl-standard

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

Query Parameters

per_page   integer  optional    

Number of items to show per page. Defaults to 100 Example: 11

page   integer  optional    

the page number to show. Example: 1

filter[name]   string  optional    

Filter column name by any accepted value. Filter by MRSL standard name Example: dolores

filter[version]   integer  optional    

Filter column version by any accepted value. Filter by MRSL standard version Example: 8

filter[conformance_level_id]   integer  optional    

Filter column conformance_level_id by any accepted value. Filter by conformance level ID Example: 6

filter[conformance_level_name]   string  optional    

Filter column conformance_level_name by any accepted value. Filter by conformance level name Example: ratione

filter[status]   string  optional    

Filter column status by any accepted value. Matches exact value. Example: FAILED

Must be one of:
  • FAILED
  • PASSED
filter[start_date]   datetime  optional    

Filter column start_date by any accepted value. Filter by start date of MRSL standard validity period Example: >= 2026-05-17 10:15:45

filter[end_date]   datetime  optional    

Filter column end_date by any accepted value. Filter by end date of MRSL standard validity period Example: <= 2026-04-29 10:15:45

filter[created_at]   datetime  optional    

Filter column created_at by any accepted value. Filter by MRSL standard created date Example: = 2026-05-19 10:15:45

filter[updated_at]   datetime  optional    

Filter column updated_at by any accepted value. Filter by MRSL standard updated date Example: < 2026-05-04 10:15:45

search   string  optional    

Search in all of these columns: name, version, conformance_level_name Example: error

sort   string  optional    

sort by any accepted column: name, version, status, conformance_level_id, conformance_level_name, start_date, end_date, created_at, updated_at. prefix a "-" before the column name to sort in descending order Example: name

column   string  optional    

get a distinct list of filterable values for any of the columns: name, conformance_level_id, conformance_level_name, version, status, start_date, end_date, created_at, updated_at. Example: name

Product Assignment

List product assignments

requires authentication

Returns a paginated list of product assignments for this certification body.

Example request:
curl --request GET \
    --get "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment?per_page=6&page=1&filter%5Bproduct_id%5D=1&filter%5Bproduct_reference_id%5D=sapiente&filter%5Bproduct_name%5D=soluta&filter%5Bproduct_alternate_names%5D=voluptas&filter%5Bassignment_status%5D=UNDER_REVIEW&filter%5Bformulator_id%5D=10&filter%5Bformulator_reference_id%5D=omnis&filter%5Bformulator_name%5D=perspiciatis&filter%5Bmrsl_level_id%5D=2&filter%5Bctz_level_id%5D=1&filter%5Bassigned_at%5D=%3C+2026-05-19+10%3A15%3A45&filter%5Bupdated_at%5D=%3C+2026-05-07+10%3A15%3A45&filter%5Bproduct_use_types_id%5D=5&filter%5Bproduct_categories_id%5D=7&search=impedit&sort=id&column=id" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment"
);

const params = {
    "per_page": "6",
    "page": "1",
    "filter[product_id]": "1",
    "filter[product_reference_id]": "sapiente",
    "filter[product_name]": "soluta",
    "filter[product_alternate_names]": "voluptas",
    "filter[assignment_status]": "UNDER_REVIEW",
    "filter[formulator_id]": "10",
    "filter[formulator_reference_id]": "omnis",
    "filter[formulator_name]": "perspiciatis",
    "filter[mrsl_level_id]": "2",
    "filter[ctz_level_id]": "1",
    "filter[assigned_at]": "< 2026-05-19 10:15:45",
    "filter[updated_at]": "< 2026-05-07 10:15:45",
    "filter[product_use_types_id]": "5",
    "filter[product_categories_id]": "7",
    "search": "impedit",
    "sort": "id",
    "column": "id",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": null,
            "product_id": 1,
            "product_reference_id": "20-NTNRYDE6A32E-I",
            "product_name": "sed",
            "product_description": null,
            "product_alternate_names": null,
            "formulator_id": 5,
            "formulator_reference_id": "01-9YKA6G6TQSUTU-T",
            "formulator_name": "test-Chemical formulator System",
            "assignment_status": null,
            "mrsl_level_id": null,
            "mrsl_level_name": null,
            "ctz_level_id": null,
            "ctz_level_name": null,
            "assigned_at": null,
            "updated_at": null
        },
        {
            "id": null,
            "product_id": 1,
            "product_reference_id": "20-NTNRYDE6A32E-I",
            "product_name": "sed",
            "product_description": null,
            "product_alternate_names": null,
            "formulator_id": 5,
            "formulator_reference_id": "01-9YKA6G6TQSUTU-T",
            "formulator_name": "test-Chemical formulator System",
            "assignment_status": null,
            "mrsl_level_id": null,
            "mrsl_level_name": null,
            "ctz_level_id": null,
            "ctz_level_name": null,
            "assigned_at": null,
            "updated_at": null
        }
    ],
    "links": {
        "first": "/?page=1",
        "last": "/?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "active": false
            },
            {
                "url": "/?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "active": false
            }
        ],
        "path": "/",
        "per_page": 100,
        "to": 2,
        "total": 2
    }
}
 

Request      

GET api/certification-bodies/v1/{organisation_id}/product-assignment

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

Query Parameters

per_page   integer  optional    

Number of items to show per page. Defaults to 100 Example: 6

page   integer  optional    

the page number to show. Example: 1

filter[product_id]   integer  optional    

Filter column product_id by any accepted value. Filter by product ID Example: 1

filter[product_reference_id]   string  optional    

Filter column product_reference_id by any accepted value. Filter by product reference ID Example: sapiente

filter[product_name]   string  optional    

Filter column product_name by any accepted value. Filter by product name Example: soluta

filter[product_alternate_names]   string  optional    

Filter column product_alternate_names by any accepted value. Filter by product alternate names Example: voluptas

filter[assignment_status]   string  optional    

Filter column assignment_status by any accepted value. Matches exact value. Example: UNDER_REVIEW

Must be one of:
  • UNDER_REVIEW
  • ACCEPTED
  • EXPIRED
  • DECLINED
filter[formulator_id]   integer  optional    

Filter column formulator_id by any accepted value. Filter by formulator (product owner organisation) ID Example: 10

filter[formulator_reference_id]   string  optional    

Filter column formulator_reference_id by any accepted value. Filter by formulator (product owner organisation) reference ID Example: omnis

filter[formulator_name]   string  optional    

Filter column formulator_name by any accepted value. Filter by formulator (product owner organisation) name Example: perspiciatis

filter[mrsl_level_id]   integer  optional    

Filter column mrsl_level_id by any accepted value. Filter by MRSL level IDs from assignment (pivot max conformance level) Example: 2

filter[ctz_level_id]   integer  optional    

Filter column ctz_level_id by any accepted value. Filter by CTZ level IDs from assignment (pivot max CTZ level) Example: 1

filter[assigned_at]   datetime  optional    

Filter column assigned_at by any accepted value. Filter by assignment created date Example: < 2026-05-19 10:15:45

filter[updated_at]   datetime  optional    

Filter column updated_at by any accepted value. Filter by assignment updated date Example: < 2026-05-07 10:15:45

filter[product_use_types_id]   integer  optional    

Filter column product_use_types_id by any accepted value. Filter by use type ID (multiple selection allowed) Example: 5

filter[product_categories_id]   integer  optional    

Filter column product_categories_id by any accepted value. Filter by product category ID (multiple selection allowed) Example: 7

search   string  optional    

Search in all of these columns: product_id, product_reference_id, product_name, product_alternate_names, formulator_name, formulator_reference_id Example: impedit

sort   string  optional    

sort by any accepted column: id, product_id, product_reference_id, product_name, product_description, product_alternate_names, formulator_id, formulator_reference_id, formulator_name, assignment_status, mrsl_level_id, mrsl_level_name, ctz_level_id, ctz_level_name, assigned_at, updated_at. prefix a "-" before the column name to sort in descending order Example: id

column   string  optional    

get a distinct list of filterable values for any of the columns: id, product_id, product_reference_id, product_name, product_description, product_alternate_names, formulator_id, formulator_reference_id, formulator_name, assignment_status, mrsl_level_id, mrsl_level_name, ctz_level_id, ctz_level_name, assigned_at, updated_at. Example: id

Update a product assignment's certifications

requires authentication

The certifications array in the request body is the complete desired state for this assignment. After a successful call, the assignment holds exactly those certifications (MRSL rows, and CTZ rows where applicable). Anything already stored on the assignment that is not included in the request is removed or deactivated server-side—omitting an existing certification therefore deletes it from the assignment's active set.

Example request:
curl --request PUT \
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/14" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"certifications\": [
        {
            \"mrsl_standard_id\": 1,
            \"ctz_standard_id\": 1,
            \"safety_data_sheet_id\": 1
        }
    ]
}"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/14"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "certifications": [
        {
            "mrsl_standard_id": 1,
            "ctz_standard_id": 1,
            "safety_data_sheet_id": 1
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "data": {
        "id": 14,
        "assignment_status": "UNDER_REVIEW",
        "product": {
            "id": 31,
            "reference_id": "20-MAMVURD727AJ-P",
            "name": "cupiditate",
            "alternate_names": [
                "alternative name"
            ],
            "description": null,
            "version": 1,
            "status": "UNDER_REVIEW",
            "formulator": {
                "id": 32,
                "name": "test-Chemical formulator m.breuer",
                "reference_id": "01-R6MYJM3NE47K4-H"
            },
            "safety_data_sheets": [
                {
                    "id": 3,
                    "file_name": "test-pdf.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 32,
                        "name": "test-Chemical formulator m.breuer",
                        "reference_id": "01-R6MYJM3NE47K4-H"
                    },
                    "file_url": null
                },
                {
                    "id": 17,
                    "file_name": "test-pdf.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 33,
                        "name": "test-ZDHC MRSL Certification Bodies m.breuer",
                        "reference_id": "01-TXF8SFVYCQAUD-H"
                    },
                    "file_url": null
                }
            ]
        },
        "current_max_mrsl_certification": null,
        "current_max_ctz_certification": null,
        "mrsl_certifications": [],
        "ctz_certifications": [],
        "assigned_at": "2025-12-12T09:13:50.000000Z",
        "updated_at": "2025-12-12T16:04:31.000000Z"
    }
}
 

Request      

PUT api/certification-bodies/v1/{organisation_id}/product-assignment/{id}

PATCH api/certification-bodies/v1/{organisation_id}/product-assignment/{id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

id   integer     

The ID of the product assignment. Example: 14

Body Parameters

certifications   object[]  optional    

Array of certifications to update. Always a combination of MRSL+Safety Data Sheet or CTZ+Safety Data Sheet.

mrsl_standard_id   integer  optional    

MRSL standard ID (integer, must exist in the database and be assigned to the requesting certification body). Example: 1

ctz_standard_id   integer  optional    

CTZ standard ID (integer, must exist in the database and be assigned to the requesting certification body). Note that the the assignment must have an MRSL certification with a conformance level of at least Level 2 from the same certification body trying to create the CTZ certification. Example: 1

safety_data_sheet_id   integer     

Safety data sheet ID (integer, must exist in the database). This will be the safety data sheet the certifications are based on. Example: 1

Bulk-update product assignments

requires authentication

Each element of certifications includes an assignment_id (product assignment row for this CB). Rows are grouped by that id: for each assignment, the rows with matching assignment_id form the complete desired state for that assignment only. After the call, each touched assignment holds exactly those certifications (same replace semantics as the single update endpoint).

The response data is a summary object (counts and IDs), not a product-assignment API resource collection.

Example request:
curl --request POST \
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/bulk-update" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"certifications\": [
        {
            \"assignment_id\": 1,
            \"mrsl_standard_id\": 1,
            \"ctz_standard_id\": 1,
            \"safety_data_sheet_id\": 1
        }
    ]
}"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/bulk-update"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "certifications": [
        {
            "assignment_id": 1,
            "mrsl_standard_id": 1,
            "ctz_standard_id": 1,
            "safety_data_sheet_id": 1
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, All submitted IDs were eligible and are now updated.):


{
    "message": "Product assignments updated successfully",
    "data": {
        "submitted_count": 2,
        "updated_count": 2,
        "updated_ids": [
            101,
            102
        ],
        "not_updateable_count": 0,
        "errors": []
    }
}
 

Example response (200, Some submitted IDs could not be updated (e.g. declined between validation and persistence); those IDs appear under `errors`. Same response shape as full success.):


{
    "message": "Product assignments partially updated",
    "data": {
        "submitted_count": 2,
        "updated_count": 1,
        "updated_ids": [
            101
        ],
        "not_updateable_count": 1,
        "errors": [
            {
                "assignment_id": 102,
                "error": "Product assignment is already declined"
            }
        ]
    }
}
 

Request      

POST api/certification-bodies/v1/{organisation_id}/product-assignment/bulk-update

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

Body Parameters

certifications   object[]  optional    

Array of certification rows. Each row includes assignment_id (product assignment for this certification body). MRSL and/or CTZ fields apply to that assignment only.

assignment_id   integer     

Assignment ID (integer, must exist in the database) and be assigned to the requesting certification body) wich you want to certify. Example: 1

mrsl_standard_id   integer  optional    

MRSL standard ID (integer, must exist in the database and be assigned to the requesting certification body). Example: 1

ctz_standard_id   integer  optional    

CTZ standard ID (integer, must exist in the database and be assigned to the requesting certification body). Note that the the assignment must have an MRSL certification with a conformance level of at least Level 2 from the same certification body trying to create the CTZ certification. Example: 1

safety_data_sheet_id   integer     

Safety data sheet ID (integer, must exist in the database). This will be the safety data sheet the certifications are based on. Example: 1

Decline a product assignment

requires authentication

Marks the assignment identified in the URL as declined for this certification body.

Example request:
curl --request POST \
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/1/decline" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Unable to process at this time.\"
}"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/1/decline"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Unable to process at this time."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "data": {
        "id": 14,
        "assignment_status": "UNDER_REVIEW",
        "product": {
            "id": 31,
            "reference_id": "20-MAMVURD727AJ-P",
            "name": "cupiditate",
            "alternate_names": [
                "alternative name"
            ],
            "description": null,
            "version": 1,
            "status": "UNDER_REVIEW",
            "formulator": {
                "id": 32,
                "name": "test-Chemical formulator m.breuer",
                "reference_id": "01-R6MYJM3NE47K4-H"
            },
            "safety_data_sheets": [
                {
                    "id": 3,
                    "file_name": "test-pdf.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 32,
                        "name": "test-Chemical formulator m.breuer",
                        "reference_id": "01-R6MYJM3NE47K4-H"
                    },
                    "file_url": null
                },
                {
                    "id": 17,
                    "file_name": "test-pdf.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 33,
                        "name": "test-ZDHC MRSL Certification Bodies m.breuer",
                        "reference_id": "01-TXF8SFVYCQAUD-H"
                    },
                    "file_url": null
                }
            ]
        },
        "current_max_mrsl_certification": null,
        "current_max_ctz_certification": null,
        "mrsl_certifications": [],
        "ctz_certifications": [],
        "assigned_at": "2025-12-12T09:13:50.000000Z",
        "updated_at": "2025-12-12T16:04:31.000000Z"
    }
}
 

Request      

POST api/certification-bodies/v1/{organisation_id}/product-assignment/{product_assignment_id}/decline

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

product_assignment_id   integer     

The ID of the product assignment. Example: 1

Body Parameters

reason   string     

Decline reason. Must not be greater than 255 characters. Example: Unable to process at this time.

Bulk-decline product assignments

requires authentication

Marks several assignments as declined in one request. Assignments that are already declined are skipped.

The response data is a summary object (counts and IDs), not a product-assignment API resource collection.

Example request:
curl --request POST \
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/bulk-decline" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"assignment_ids\": [
        101
    ],
    \"reason\": \"Unable to process at this time.\"
}"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/bulk-decline"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "assignment_ids": [
        101
    ],
    "reason": "Unable to process at this time."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, All submitted IDs were eligible and are now declined.):


{
    "message": "Product assignments declined successfully",
    "data": {
        "submitted_count": 2,
        "declined_count": 2,
        "declined_ids": [
            101,
            102
        ],
        "already_declined_count": 0,
        "already_declined_ids": []
    }
}
 

Example response (200, Some IDs were already declined; they are listed under `already_declined_ids` (same response shape).):


{
    "message": "Product assignments partially declined",
    "data": {
        "submitted_count": 2,
        "declined_count": 1,
        "declined_ids": [
            101
        ],
        "already_declined_count": 1,
        "already_declined_ids": [
            102
        ]
    }
}
 

Request      

POST api/certification-bodies/v1/{organisation_id}/product-assignment/bulk-decline

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

Body Parameters

assignment_ids   integer[]     

Single assignment ID (integer, distinct, must exist in the the database and be assigned to the requesting certification body).

reason   string     

Shared decline reason for every assignment that changes state. Must not be greater than 255 characters. Example: Unable to process at this time.

Get a product assignment

requires authentication

Returns one assignment, including related certification and product data.

Example request:
curl --request GET \
    --get "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/14" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/14"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "id": 14,
        "assignment_status": "UNDER_REVIEW",
        "product": {
            "id": 31,
            "reference_id": "20-MAMVURD727AJ-P",
            "name": "cupiditate",
            "alternate_names": [
                "alternative name"
            ],
            "description": null,
            "version": 1,
            "status": "UNDER_REVIEW",
            "formulator": {
                "id": 32,
                "name": "test-Chemical formulator m.breuer",
                "reference_id": "01-R6MYJM3NE47K4-H"
            },
            "safety_data_sheets": [
                {
                    "id": 3,
                    "file_name": "test-pdf.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 32,
                        "name": "test-Chemical formulator m.breuer",
                        "reference_id": "01-R6MYJM3NE47K4-H"
                    },
                    "file_url": null
                },
                {
                    "id": 17,
                    "file_name": "test-pdf.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 33,
                        "name": "test-ZDHC MRSL Certification Bodies m.breuer",
                        "reference_id": "01-TXF8SFVYCQAUD-H"
                    },
                    "file_url": null
                }
            ]
        },
        "current_max_mrsl_certification": null,
        "current_max_ctz_certification": null,
        "mrsl_certifications": [],
        "ctz_certifications": [],
        "assigned_at": "2025-12-12T09:13:50.000000Z",
        "updated_at": "2025-12-12T16:04:31.000000Z"
    }
}
 

Request      

GET api/certification-bodies/v1/{organisation_id}/product-assignment/{id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

id   integer     

The ID of the product assignment. Example: 14

Download a safety data sheet

requires authentication

Returns the SDS file for the chemical product linked to the given assignment.

Example request:
curl --request GET \
    --get "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/sint/safety-data-sheet/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/sint/safety-data-sheet/1"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


file
 

Request      

GET api/certification-bodies/v1/{organisation_id}/product-assignment/{product_assignment_id}/safety-data-sheet/{safetyDataSheet_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_id   integer     

The ID of the organisation. Example: 1

product_assignment_id   string     

The ID of the product assignment. Example: sint

safetyDataSheet_id   integer     

The ID of the safetyDataSheet. Example: 1