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 Level

List CTZ levels

requires authentication

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

Example request:
curl --request GET \
    --get "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/ctz-level?per_page=18&page=1&filter%5Bname%5D=quisquam&filter%5Bcreated_at%5D=%3C%3D+2026-07-01+10%3A59%3A25&filter%5Bupdated_at%5D=%3E%3D+2026-07-09+10%3A59%3A25&search=dicta&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-level"
);

const params = {
    "per_page": "18",
    "page": "1",
    "filter[name]": "quisquam",
    "filter[created_at]": "<= 2026-07-01 10:59:25",
    "filter[updated_at]": ">= 2026-07-09 10:59:25",
    "search": "dicta",
    "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": 1,
            "name": "Foundational"
        },
        {
            "id": 1,
            "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-level

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: 18

page   integer  optional    

the page number to show. Example: 1

filter[name]   string  optional    

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

filter[created_at]   datetime  optional    

Filter column created_at by any accepted value. Filter by CTZ level created date Example: <= 2026-07-01 10:59:25

filter[updated_at]   datetime  optional    

Filter column updated_at by any accepted value. Filter by CTZ level updated date Example: >= 2026-07-09 10:59:25

search   string  optional    

Search in all of these columns: name. Filter type: like. Example: dicta

sort   string  optional    

sort by any accepted column: name, 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, 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=20&page=1&filter%5Bname%5D=rem&filter%5Bversion%5D=5&filter%5Bconformance_level_id%5D=3&filter%5Bconformance_level_name%5D=dolorem&filter%5Bstatus%5D=FAILED&filter%5Bstart_date%5D=%3D+2026-07-17+10%3A59%3A25&filter%5Bend_date%5D=%3C+2026-06-29+10%3A59%3A25&filter%5Bcreated_at%5D=%3E%3D+2026-07-01+10%3A59%3A25&filter%5Bupdated_at%5D=%3E%3D+2026-06-29+10%3A59%3A25&search=minima&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": "20",
    "page": "1",
    "filter[name]": "rem",
    "filter[version]": "5",
    "filter[conformance_level_id]": "3",
    "filter[conformance_level_name]": "dolorem",
    "filter[status]": "FAILED",
    "filter[start_date]": "= 2026-07-17 10:59:25",
    "filter[end_date]": "< 2026-06-29 10:59:25",
    "filter[created_at]": ">= 2026-07-01 10:59:25",
    "filter[updated_at]": ">= 2026-06-29 10:59:25",
    "search": "minima",
    "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": 1,
            "reference_id": "30-HRBQFLXEEXU-K",
            "name": "Brachi Testing Services",
            "version": "ZDHC MRSL v2.0",
            "start_date": "2020-08-12T00:00:00.000000Z",
            "end_date": "2024-06-01T00:00:00.000000Z",
            "status": "PASSED",
            "conformance_level_id": 1,
            "conformance_level_name": "Level 1"
        },
        {
            "id": 1,
            "reference_id": "30-HRBQFLXEEXU-K",
            "name": "Brachi Testing Services",
            "version": "ZDHC MRSL v2.0",
            "start_date": "2020-08-12T00:00:00.000000Z",
            "end_date": "2024-06-01T00:00:00.000000Z",
            "status": "PASSED",
            "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: 20

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: rem

filter[version]   integer  optional    

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

filter[conformance_level_id]   integer  optional    

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

filter[conformance_level_name]   string  optional    

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

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-07-17 10:59:25

filter[end_date]   datetime  optional    

Filter column end_date by any accepted value. Filter by end date of MRSL standard validity period Example: < 2026-06-29 10:59:25

filter[created_at]   datetime  optional    

Filter column created_at by any accepted value. Filter by MRSL standard created date Example: >= 2026-07-01 10:59:25

filter[updated_at]   datetime  optional    

Filter column updated_at by any accepted value. Filter by MRSL standard updated date Example: >= 2026-06-29 10:59:25

search   string  optional    

Search in all of these columns: name, version, conformance_level_name. Filter type: like. Example: minima

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=8&page=1&filter%5Bproduct_id%5D=19&filter%5Bproduct_reference_id%5D=repudiandae&filter%5Bproduct_name%5D=et&filter%5Bproduct_alternate_names%5D=quasi&filter%5Bassignment_status%5D=UNDER_REVIEW&filter%5Bformulator_id%5D=6&filter%5Bformulator_reference_id%5D=quam&filter%5Bformulator_name%5D=officiis&filter%5Bmrsl_level_id%5D=3&filter%5Bctz_level_id%5D=17&filter%5Bassigned_at%5D=%3D+2026-07-07+10%3A59%3A25&filter%5Bupdated_at%5D=%3E%3D+2026-07-13+10%3A59%3A25&filter%5Bproduct_use_types_id%5D=11&filter%5Bproduct_categories_id%5D=16&search=est&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": "8",
    "page": "1",
    "filter[product_id]": "19",
    "filter[product_reference_id]": "repudiandae",
    "filter[product_name]": "et",
    "filter[product_alternate_names]": "quasi",
    "filter[assignment_status]": "UNDER_REVIEW",
    "filter[formulator_id]": "6",
    "filter[formulator_reference_id]": "quam",
    "filter[formulator_name]": "officiis",
    "filter[mrsl_level_id]": "3",
    "filter[ctz_level_id]": "17",
    "filter[assigned_at]": "= 2026-07-07 10:59:25",
    "filter[updated_at]": ">= 2026-07-13 10:59:25",
    "filter[product_use_types_id]": "11",
    "filter[product_categories_id]": "16",
    "search": "est",
    "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-TZDRC62ZX2PA-B",
            "product_name": "A-POLE G-2 [P214AG76]",
            "product_description": null,
            "product_alternate_names": [
                "P214AG76"
            ],
            "formulator_id": 470,
            "formulator_reference_id": "01-QAX9HUTDPHX-G",
            "formulator_name": "JiangYin Xishiqiao Auxiliary Factory",
            "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-TZDRC62ZX2PA-B",
            "product_name": "A-POLE G-2 [P214AG76]",
            "product_description": null,
            "product_alternate_names": [
                "P214AG76"
            ],
            "formulator_id": 470,
            "formulator_reference_id": "01-QAX9HUTDPHX-G",
            "formulator_name": "JiangYin Xishiqiao Auxiliary Factory",
            "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: 8

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: 19

filter[product_reference_id]   string  optional    

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

filter[product_name]   string  optional    

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

filter[product_alternate_names]   string  optional    

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

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: 6

filter[formulator_reference_id]   string  optional    

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

filter[formulator_name]   string  optional    

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

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: 3

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: 17

filter[assigned_at]   datetime  optional    

Filter column assigned_at by any accepted value. Filter by assignment created date Example: = 2026-07-07 10:59:25

filter[updated_at]   datetime  optional    

Filter column updated_at by any accepted value. Filter by assignment updated date Example: >= 2026-07-13 10:59:25

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: 11

filter[product_categories_id]   integer  optional    

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

search   string  optional    

Search in all of these columns: product_id, product_reference_id, product_name, product_alternate_names, formulator_name, formulator_reference_id. Filter type: like. Example: est

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

Bulk-create product assignment certifications

requires authentication

Each row in certifications creates one new MRSL+CTZ certification pair on the product assignment identified by assignment_id. Existing certifications on an assignment are not modified or removed.

Multiple rows may reference the same assignment_id; each row adds another certification pair.

The response data summarizes by unique assignment_id: submitted_count is the number of distinct assignment IDs in the request; created_count and created_ids count assignments where at least one row was created successfully. Per-row failures are listed in errors.

Example request:
curl --request POST \
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/bulk-store" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"certifications\": [
        {
            \"certificate_number\": \"1234567890\",
            \"certificate_result\": \"Pass\",
            \"certificate_issue_date\": \"2026-01-01\",
            \"certificate_end_date\": \"2026-01-01\",
            \"assignment_id\": 1,
            \"mrsl_standard_id\": 1,
            \"ctz_level_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-store"
);

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

let body = {
    "certifications": [
        {
            "certificate_number": "1234567890",
            "certificate_result": "Pass",
            "certificate_issue_date": "2026-01-01",
            "certificate_end_date": "2026-01-01",
            "assignment_id": 1,
            "mrsl_standard_id": 1,
            "ctz_level_id": 1,
            "safety_data_sheet_id": 1
        }
    ]
};

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

Example response (200, Every distinct assignment ID in the request received at least one new certification.):


{
    "message": "Product assignment certifications created successfully",
    "data": {
        "submitted_count": 2,
        "created_count": 2,
        "created_ids": [
            101,
            102
        ],
        "not_createable_count": 0,
        "errors": []
    }
}
 

Example response (200, Some assignment IDs could not receive new certifications (e.g. declined assignment); failed rows appear under `errors`. Same response shape as full success.):


{
    "message": "Product assignment certifications partially created",
    "data": {
        "submitted_count": 2,
        "created_count": 1,
        "created_ids": [
            101
        ],
        "not_createable_count": 1,
        "errors": [
            {
                "assignment_id": 102,
                "error": "Product assignment is already declined"
            }
        ]
    }
}
 

Request      

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

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[]     

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

certificate_number   string  optional    

Optional certificate number (string, max 255 characters). Omit or leave empty to store null. Example: 1234567890

certificate_result   string  optional    

Optional certificate result (string, max 255 characters). Omit or leave empty to store null. Example: Pass

certificate_issue_date   string  optional    

Certificate issue date (date). Example: 2026-01-01

certificate_end_date   string  optional    

Optional certificate end date (date). Omit or leave empty to store null; gateway_active_until defaults to three years from now. Example: 2026-01-01

assignment_id   string  optional    

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   string  optional    

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

ctz_level_id   string  optional    

CTZ level ID (integer, must exist in the database).

Allowed MRSL/CTZ combinations:

  • MRSL levels 1, 2, and 3 may all use CTZ level 1
  • MRSL level 1: CTZ level 1 only
  • MRSL level 2: CTZ levels 1 or 2
  • MRSL level 3: CTZ levels 1, 2, or 3

CTZ level ID 1 is always available to certification bodies; higher CTZ levels must be assigned to the requesting certification body. Example: 1

safety_data_sheet_id   string  optional    

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

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.

Create a product assignment certification

requires authentication

Creates one MRSL certification together with its linked CTZ certification on the given assignment.

Example request:
curl --request POST \
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"certificate_number\": \"1234567890\",
    \"certificate_result\": \"Pass\",
    \"certificate_issue_date\": \"2026-01-01\",
    \"certificate_end_date\": \"2026-01-01\",
    \"mrsl_standard_id\": 1,
    \"ctz_level_id\": 1,
    \"safety_data_sheet_id\": 1
}"
const url = new URL(
    "https://staging-api.vm400.consulting1x1.info/api/certification-bodies/v1/1/product-assignment/1"
);

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

let body = {
    "certificate_number": "1234567890",
    "certificate_result": "Pass",
    "certificate_issue_date": "2026-01-01",
    "certificate_end_date": "2026-01-01",
    "mrsl_standard_id": 1,
    "ctz_level_id": 1,
    "safety_data_sheet_id": 1
};

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

Example response (200):


{
    "data": {
        "id": 1,
        "assignment_status": "ACCEPTED",
        "product": {
            "id": 212878,
            "reference_id": "20-4G9DTFRJWQ72-I",
            "name": "REACTOBOND YELLOW RNL 150% [P397IA55]",
            "alternate_names": [
                "P397IA55"
            ],
            "description": null,
            "version": 1,
            "status": "PUBLISHED",
            "formulator": {
                "id": 6267,
                "name": "Meghmani Dyes and Intermediates LLP",
                "reference_id": "01-76N8M6UUCZG-Y"
            },
            "safety_data_sheets": [
                {
                    "id": 84327,
                    "file_name": "MSDS YELLOW RNL 150%.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 6267,
                        "name": "Meghmani Dyes and Intermediates LLP",
                        "reference_id": "01-76N8M6UUCZG-Y"
                    },
                    "file_url": null
                }
            ],
            "sds_url": null,
            "website_url": null
        },
        "current_max_mrsl_certification": null,
        "current_max_ctz_certification": null,
        "mrsl_certifications": [],
        "assigned_at": "2026-05-14T19:42:10.000000Z",
        "updated_at": "2026-05-14T19:42:10.000000Z"
    }
}
 

Request      

POST 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: 1

product_assignment   integer     

The ID of the product assignment. Example: 1

Body Parameters

certificate_number   string  optional    

Optional certificate number (string, max 255 characters). Omit or leave empty to store null. Example: 1234567890

certificate_result   string  optional    

Optional certificate result (string, max 255 characters). Omit or leave empty to store null. Example: Pass

certificate_issue_date   string  optional    

Certificate issue date (date). Example: 2026-01-01

certificate_end_date   string  optional    

Optional certificate end date (date). Omit or leave empty to store null; gateway_active_until defaults to three years from now. Example: 2026-01-01

mrsl_standard_id   string  optional    

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

ctz_level_id   string  optional    

CTZ level ID (integer, must exist in the database).

Allowed MRSL/CTZ combinations:

  • MRSL levels 1, 2, and 3 may all use CTZ level 1
  • MRSL level 1: CTZ level 1 only
  • MRSL level 2: CTZ levels 1 or 2
  • MRSL level 3: CTZ levels 1, 2, or 3

CTZ level ID 1 is always available to certification bodies; higher CTZ levels must be assigned to the requesting certification body. Example: 1

safety_data_sheet_id   string  optional    

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": 1,
        "assignment_status": "ACCEPTED",
        "product": {
            "id": 212878,
            "reference_id": "20-4G9DTFRJWQ72-I",
            "name": "REACTOBOND YELLOW RNL 150% [P397IA55]",
            "alternate_names": [
                "P397IA55"
            ],
            "description": null,
            "version": 1,
            "status": "PUBLISHED",
            "formulator": {
                "id": 6267,
                "name": "Meghmani Dyes and Intermediates LLP",
                "reference_id": "01-76N8M6UUCZG-Y"
            },
            "safety_data_sheets": [
                {
                    "id": 84327,
                    "file_name": "MSDS YELLOW RNL 150%.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 6267,
                        "name": "Meghmani Dyes and Intermediates LLP",
                        "reference_id": "01-76N8M6UUCZG-Y"
                    },
                    "file_url": null
                }
            ],
            "sds_url": null,
            "website_url": null
        },
        "current_max_mrsl_certification": null,
        "current_max_ctz_certification": null,
        "mrsl_certifications": [],
        "assigned_at": "2026-05-14T19:42:10.000000Z",
        "updated_at": "2026-05-14T19:42:10.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.

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/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/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):


{
    "data": {
        "id": 1,
        "assignment_status": "ACCEPTED",
        "product": {
            "id": 212878,
            "reference_id": "20-4G9DTFRJWQ72-I",
            "name": "REACTOBOND YELLOW RNL 150% [P397IA55]",
            "alternate_names": [
                "P397IA55"
            ],
            "description": null,
            "version": 1,
            "status": "PUBLISHED",
            "formulator": {
                "id": 6267,
                "name": "Meghmani Dyes and Intermediates LLP",
                "reference_id": "01-76N8M6UUCZG-Y"
            },
            "safety_data_sheets": [
                {
                    "id": 84327,
                    "file_name": "MSDS YELLOW RNL 150%.pdf",
                    "version": 1,
                    "locale": "en",
                    "uploaded_by": {
                        "id": 6267,
                        "name": "Meghmani Dyes and Intermediates LLP",
                        "reference_id": "01-76N8M6UUCZG-Y"
                    },
                    "file_url": null
                }
            ],
            "sds_url": null,
            "website_url": null
        },
        "current_max_mrsl_certification": null,
        "current_max_ctz_certification": null,
        "mrsl_certifications": [],
        "assigned_at": "2026-05-14T19:42:10.000000Z",
        "updated_at": "2026-05-14T19:42:10.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: 1

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/hic/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/hic/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: hic

safetyDataSheet_id   integer     

The ID of the safetyDataSheet. Example: 1