Skip to main content
  • Software

SDS Authoring Software

Global compliance requirements are becoming more complex around hazard communication. Get the SDS authoring support you need to create, maintain and distribute compliant safety data sheets and labels.

Illustrative SDS pictogram with fire icon

Automate SDS authoring with ULTRUS™ software

With a global team of over 70 regulatory and software experts, UL Solutions is ready to help you efficiently create and manage compliant safety data sheets (SDS) and labels.

You’re developing new products, entering new markets and trying to satisfy the demands of customers around the world. But as the number of regulations increases, so does your workload and the complexity of staying in compliance. You need SDS authoring software to improve accuracy, reduce risk and save time.

As part of ULTRUS™ software, you’ll have access to powerful tools that simplify your chemical compliance and data management activities, including automating SDS authoring, distribution and hazard labeling.

Schedule a demo

 

Comprehensive SDS authoring software and regulatory information

 

Person in PPE next to SDS pictogram symbols


Regulatory content database

Easily source chemical and regulatory information with our regulatory content database, ChemADVISOR®. With more than 8,000 regulatory lists and data on more than 600,000 substances, you can confidently maintain compliance in the markets where you do business. Learn more about ChemADVISOR.

Drawing of a flask next to a Safety Data Sheet


Multilingual hazard communication

Our software has more than 25,000 safety phrases translated into 48 languages. This allows you to efficiently create multilingual hazard communication documents in the jurisdictions and languages you need.

Two people reviewing their chemical stock


Label generation

Easily create multilingual and multijurisdictional labels that comply with regulatory requirements. With just a few clicks in the software, you can design, publish and print labels that meet your business needs.

Illustration showing a Bill of Materials and a Safety Data Sheet as well as a flowchart


GHS classification and transport data automation

Simplify the classification of products and components for multiple countries and more easily populate relevant SDS sections. Efficiently complete section 14 transport data for multiple countries through streamlined selection of the UN number. 

Illustration of SDS documents being created by EHS professional and shared with worker


Internal and external publishing

Share SDSs using our fully customizable publishing tool. You can update the web-viewer interface to use your company branding or adjust the layout. In addition, you’re in control of who can access specific documents both internally and externally.

Flexible SDS authoring software integrates with your systems

With our configurable and flexible software, businesses of all sizes can build beyond SDS authoring to a full chemical data management platform. In addition, our software can integrate with your enterprise resource planning (ERP), product lifecycle management (PLM) or other third-party systems allowing you to connect important information and data.

Learn more about our chemical compliance management software

Frequently asked questions about our SDS authoring software

How can I access your SDS authoring software?

You can access the software through a web-based application that can be installed on your server or on a UL Solutions server.

Why does every user of the SDS software have their own user ID and password?

This allows for a better auditing trail and data maintenance by showing where the entered data came from. It also allows for each user to have different levels of access and permissions. 

English is not the first language of everyone on my team. Is your SDS authoring software interface translated?

Yes, our interface is translated into 17 languages. In addition, all SDS phrases are translated into every language we offer. This enables you to publish any template in any language for easier internal reviews. 

How can I enter product data into the software?

There are many options for data entry into the SDS authoring software. You can manually enter new data, or we can migrate data from an existing system or integrate it with your ERP. In addition, you can copy data from an existing product as a starting point. Importantly, you can build formulations at the CAS level with exact concentrations of each substance, or at a bill of materials (BOM) level. This allows you to enter raw materials that each have constituent formulations. 

Do you provide regulatory data and content?

Yes, our ChemADVISOR regulatory database integrates with our SDS authoring software. You can use structured chemical data sourced from a database like ChemADVISOR in addition to using your own content.

What formats can I use to publish my SDSs using the software?

You can publish in PDF, RFT or HTML formats. You can view, email or print directly from the software. We offer a web viewer tool for viewing documents online. This enables you to share documents with customers or employees. You have flexibility and control over which documents are visible, how the interface appears and what filters are available.   
 

 

Mitigate risk with SDS authoring software

UL Solutions has been supporting customers with product stewardship and SDS authoring services for nearly 40 years. Our SDS authoring software can enhance your team’s efficiency, consistency and accuracy.

 

 

Powerful data solutions for global businesses

Line of colored rectangles with two selected

WERCS Studio chemical data and compliance management software

This platform is designed to help you achieve regulatory compliance, supply chain transparency and meet your sustainability goals.

Learn more
Illustration of a graph

Supply Chain Network

Make critical business decisions with greater speed and confidence with Supply Chain Network. This digital solution helps you request robust supplier information from your partners around the world, improve data quality, and respond quickly to changes in formulation, physical properties or regulations.

Learn more
Illustration of a bar graph with one of the bars enhanced

Illuminator regulation monitoring software

Now you can access the comprehensive ChemADVISOR Regulatory Database, personalized to your unique product portfolio and regulatory needs to facilitate streamlined compliance activities and get products to market.


Learn more

Regulatory Roundup Newsletter

Stay up to date on chemical regulatory news

Subscribe to the Regulatory Roundup Newsletter and stay up to date on current and upcoming regulations and all the latest chemical industry news.

Subscribe now
X

Hazard Communication is complicated. We make it simpler.

Please wait…
'; let submitButton = $('form.mktoForm .mktoButtonWrap .mktoButton'); submitButton.prop("disabled", true); submitButton.append(loadingHTML); // Ocp-Apim-Subscription-Key: '***'. var $ocpKey = drupalSettings.ul_marketo_validate_key; var $ocpUrl = drupalSettings.ul_marketo_validate_url; var $ocpEnv = drupalSettings.ul_marketo_validate_env; if (submitCount > 1 || isResponseSuccess == true) { isResponseHandled = true; form.submittable(true); if (paramsDebug.debug == 1) { console.log("NO.10 submitCount > 1: form.submit() "); } submitButton.click(); } else { var email = form.vals().Email; var phone = (form.vals().Phone) ? (form.vals().Phone) : '18472728800'; var country = (form.vals().Country) ? (form.vals().Country) : 'United States'; var countryCode = (phone == '18472728800') ? 'US' : getCountryCode(country); var $emailMsg = drupalSettings.ul_marketo_validate.email; var $phoneMsg = drupalSettings.ul_marketo_validate.phone; if (paramsDebug.debug == 1) { console.log("paramsDebug:"); console.log(paramsDebug); } sendingData = JSON.stringify({ 'Input_Email': email, 'Input_Phone': phone, 'Input_ISO2_Country_Code': countryCode }); if (paramsDebug.debug == 1) { console.log('URL, debug missing button:'); console.log($ocpUrl); console.log("Sending Data to API service (ajax):"); console.log(sendingData); } // API call for validate email/phone/country. var sendDate = (new Date()).getTime(); var responseTimeMs = sendDate; // Start a timer to handle a 3-second response timeout setTimeout(function() { if (!isResponseHandled) { isResponseHandled = true; // Handle the assumed "success" due to timeout here if (paramsDebug.debug == 1) { console.log('No response within timeout, proceeding with default validation success.'); } $('#ValidMsgEmail').remove(); $('#ValidMsgPhone').remove(); // set form to be submittable here $('#valSpinner').remove(); submitButton.prop("disabled", false); // Setup status as Timeout instead of Unknown. emailAddressStatus = "Timeout"; phoneNumberStatus = "Timeout"; phoneNumberValidated = "Timeout"; form.vals({ "emailAddressStatus": emailAddressStatus, "phoneNumberStatus": phoneNumberStatus, "phoneNumberValidated": phoneNumberValidated, }); form.submittable(true); isResponseSuccess = true; if (paramsDebug.debug == 1) { let receiveDate = (new Date()).getTime(); responseTimeMs = (receiveDate - sendDate) / 1000; console.log("setTimeout: " + responseTimeMs + " seconds."); } submitButton.click(); } }, paramsDebug.timeout * 1000); //END setTimeout $.ajax({ url: $ocpUrl, type: 'POST', data: sendingData, headers: { 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': $ocpKey, }, success: function (response) { $('#valSpinner').remove(); submitButton.prop("disabled", false); phoneNumberValidated = response.Validated_Phone_Number; if (!isResponseHandled) { isResponseHandled = true; // **** Handle the validation response if (paramsDebug.debug == 1) { console.log("API Response Data: "); console.log(response); } var emailCode = response.Email_Validation_Status_Number; var phoneCode = response.Phone_Validation_Status_Number; // Add values into two status fields. if (emailValidCode.hasOwnProperty(emailCode)) { emailAddressStatus = emailValidCode[emailCode]; } else { // API error code doesn't exist, set defaul. emailAddressStatus = emailValidCode['300']; } if (phoneValidCode.hasOwnProperty(phoneCode)) { phoneNumberStatus = phoneValidCode[phoneCode]; } else { // API error code doesn't exist, set defaul. phoneNumberStatus = phoneValidCode['301']; } if (emailAddressStatus.indexOf("email_not") != -1) { emailAddressStatus = 'Invalid'; } else if(emailAddressStatus == 'valid') { emailAddressStatus = 'Valid'; } if (phoneNumberStatus.indexOf('invalid_') != -1) { phoneNumberStatus = 'Invalid'; } else if(phoneNumberStatus == 'valid') { phoneNumberStatus = 'Valid'; } if (paramsDebug.debug == 1) { console.log('form.emailAddressStatus ' + emailAddressStatus); console.log('form.phoneNumberStatus ' + phoneNumberStatus); console.log('form.phoneNumberValidated ' + phoneNumberValidated); } if (validEmailCode.includes(emailCode) && validPhoneCode.includes(phoneCode)) { // Remove the loading spinner. $('#ValidMsgEmail').remove(); $('#ValidMsgPhone').remove(); $('#valSpinner').remove(); submitButton.prop("disabled", false); if (paramsDebug.debug == 1) { console.log("NO.1.1 VALID OK submitCount = " + submitCount); } isResponseSuccess = true; form.vals({ "emailAddressStatus": emailAddressStatus, "phoneNumberStatus": phoneNumberStatus, "phoneNumberValidated": phoneNumberValidated, }); form.submittable(true); submitButton.click(); } // API valid code is not "valid". else { form.submittable(false); if (paramsDebug.debug == 1) { console.log("NO.1.2 Submittable false : count= " + submitCount + " validateOrigin " + validateOrigin + " isResponseSuccess " + isResponseSuccess ); } // Set the invalid message in language translation. var msgEmailStatus = ""; if (!validEmailCode.includes(emailCode)) { if (emailCode == '300' || emailCode == '400') { msgEmailStatus = $emailMsg.email_not_valid; } else if (emailCode == '310' || emailCode == '500' ) { msgEmailStatus = $emailMsg.email_not_accept; } else { msgEmailStatus = "Unknown"; } if (paramsDebug.debug == 1) { console.log("NOT validEmailCode: " + msgEmailStatus); } errEmail = ''; $('#Email').after(errEmail); form.submittable(false); } // Set the invalid message in language translation. var msgPhoneStatus = "" if (!validPhoneCode.includes(phoneCode)) { msgPhoneStatus = $phoneMsg[phoneValidCode[phoneCode]]; if (paramsDebug.debug == 1) { console.log("NOT validPhoneCode: " + msgPhoneStatus); } errPhone = ''; $('#Phone').after(errPhone); form.submittable(false); } if (paramsDebug.debug == 1) { console.log(msgEmailStatus); console.log(msgPhoneStatus); } // Remove the loading spinner. $('#valSpinner').remove(); submitButton.prop("disabled", false); if (paramsDebug.debug == 1) { console.log("NO.1.6 Submittable false, count= " + submitCount); } form.vals({ "emailAddressStatus": emailAddressStatus, "phoneNumberStatus": phoneNumberStatus, "phoneNumberValidated": phoneNumberValidated, }); // 2nd API call and subit form. if (submitCount >= 1) { $('#ValidMsgEmail').remove(); $('#ValidMsgPhone').remove(); isResponseSuccess = true; form.submittable(true); if (paramsDebug.debug == 1) { console.log("NO.1.8 : 2nd Submit:: submittable=true && count=1 : " + submitCount); } if (submitCount==1) { submitButton.click(); } } } //END if{} else{}. }//END if (!isResponseHandled) // Calculate the time comsumed for the API call. if (paramsDebug.debug == 1) { let receiveDate = (new Date()).getTime(); responseTimeMs = (receiveDate - sendDate) / 1000; console.log("NO.1.9 : AJAX success: Time for API call: " + responseTimeMs + " seconds."); } submitCount++; }, //END success: function(); // API call error response. error: function (error) { $('#valSpinner').remove(); submitButton.prop("disabled", false); if (!isResponseHandled) { isResponseHandled = true; $('#valSpinner').remove(); submitButton.prop("disabled", false); // 2nd API call and subit form. if ( submitCount >= 1 ) { $('#ValidMsgEmail').remove(); $('#ValidMsgPhone').remove(); form.submittable(true); isResponseSuccess = true; if ( submitCount == 1 ) { form.submittable(true); } } // Handle AJAX error if (paramsDebug.debug == 1) { console.log('**** 2 ajax error. submitCount = ' + submitCount); console.log(error); } // Calculate the time comsumed for the API call. if (paramsDebug.debug == 1) { let receiveDate = (new Date()).getTime(); responseTimeMs = (receiveDate - sendDate) / 1000; console.log("NO.2.3 AJAX error: Time for API call: " + responseTimeMs + " seconds."); } } submitCount++; // Error status 500, then submit the form. submitButton.click(); } //END error: function(); }); //END $.ajax; if (isResponseSuccess) { if (paramsDebug.debug == 1) { console.log("NO.8.0 submittable(true)"); } form.submittable(true); } } //END: if (submitCount > 1) else // Setup the form.vals and form.submittable; form.vals({ "emailAddressStatus": emailAddressStatus, "phoneNumberStatus": phoneNumberStatus, "phoneNumberValidated": phoneNumberValidated, }); } //END: if (validateOrigin && (marketoBundle)) else { // For Event form and Newsletter form. if (validateOrigin === true){ form.submittable(true); } } }); //END form.onValidate() // Success callback() form.onSuccess(function(values, followUpUrl){ // Debug Phone/Email validation. if (paramsDebug.debug == 1) { console.log("NO.9.1 onSuccess: submittable = " + form.submittable()); console.log(form.getValues()); } // Track analytics. if (typeof dataLayer !== 'undefined'){ dataLayer.push({ event: drplMkto.dataLayerEvent, mktoFormId: form.getId(), 'FormValues': cleanFormVals(form.getValues()), 'FormFields': form.allFieldsFilled(), 'Submittable': form.submittable() }); } // If function exists, delete UTM cookie: if(typeof _deleteUtmCookie === "function"){ _deleteUtmCookie(); } // Marketo Modal "Thank You" message: if(use_post_submit_mssg){ $('html, body').animate({ scrollTop: 0 }, 'slow'); $('.mkto-presubmit').addClass('hidden'); $('.mktoModalContent').addClass('mkto_thnx_center'); $('.mktoModalMask').addClass('not_clicable'); $('.mkto-postsubmit').removeClass('hidden'); $('.mktoButton').removeAttr('disabled').text(drupalSettings.marketo.button_text); $('.mktoForm')[0].reset(); grecaptcha.reset(); } // Else, redirect user: else { window.location.href = drplMkto.success_url; } // IMPORTANT: Return false to prevent further code execution. return false; }); //END form.onSuccess. }); })(jQuery, drupalSettings.marketo);