Summary

Use the ccTLD registration requirements API to fetch a per-TLD JSON Schema, build registration forms that adapt to conditional fields, and validate registrant data before you submit. Catch 400 errors before your users ever see them.

A .com registration form can often use a relatively standard set of registrant fields. A .ca registration has additional requirements, including legal_type, and for certain legal_type values, companyName becomes mandatory too. Build your application around a schema-driven approach rather than hard-coded assumptions, because a form that doesn't collect the data required by a particular ccTLD can lead to unexplained 400 errors. Schema-driven validation can catch many of these problems before the request reaches the API.

This guide walks through how to use requirementsV2, the name.com ccTLD registration requirements API endpoint, to fetch a per-TLD JSON Schema at runtime, use it to determine applicable registrant fields and conditional requirements, validate registrant payloads locally before submission, and provide useful feedback when API-side validation still rejects a request. Whether you're adding ccTLD support for the first time or hardening an existing flow, the pattern is the same.

What the requirementsV2 endpoint returns

The requirementsV2 endpoint returns a JSON Schema Draft-07 document describing the fields, allowed values, formats, patterns, and conditional requirements defined for a given TLD. Fetch it for the selected TLD:

curl -u "$NAMECOM_USERNAME:$NAMECOM_TOKEN" "https://api.dev.name.com/core/v1/domaininfo/requirementsV2/ca"

Our API uses HTTP Basic Auth with your API username and token. For sandbox testing, append -test to your username and point requests at https://api.dev.name.com. Our full sandbox setup instructions are at docs.name.com.

A condensed .ca response looks like this:

JSON
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "contacts": {
      "type": "object",
      "properties": {
        "registrant": {
          "type": "object",
          "properties": {
            "companyName": {
              "type": "string"
            },
            "zip": {
              "type": "string",
              "pattern": "^[A-Za-z]\\d[A-Za-z] \\d[A-Za-z]\\d$"
            }
          },
          "required": [
            "state",
            "zip",
            …
            "country"
          ]
        }
      },
      "required": [
        "registrant"
      ]
    },
    "tldRequirements": {
      "type": "object",
      "properties": {
        "lang_pref": {
          "oneOf": [
            {
              "const": "EN",
              "title": "English"
            },
            {
              "const": "FR",
              "title": "French"
            }
          ]
        },
        "legal_type": {
          "oneOf": [
            {
              "const": "CCO",
              "title": "Canadian Corporation"
            },
            {
              "const": "CCT",
              "title": "Canadian Citizen"
            },
            {
              "const": "RES",
              "title": "Permanent Resident"
            }
          ]
        }
      },
      "required": [
        "lang_pref",
        "legal_type"
      ]
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "tldRequirements": {
            "properties": {
              "legal_type": {
                "enum": [
                  "ASS",
                  "CCO",
      …
                  "TRS"
                ]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "contacts": {
            "properties": {
              "registrant": {
                "required": [
                  "companyName"
                ]
              }
            }
          }
        }
      }
    }
  ]
}

The response also includes a tldInfo object containing TLD metadata such as registration-year options, domain-length limits, transfer-lock support, DNSSEC support, and registry information. The companyName field appears in properties without being part of the base registrant.required array; the allOf conditional is what adds it to the required fields for the specified legal_type values. Fetch the schema for the selected TLD rather than maintaining a separate hard-coded list of TLD-specific rules.

How conditional fields work in the schema

Conditional requirements in the requirementsV2 schema work through several schema constructs that combine to describe what a particular registrant must provide.

The required array at each object level lists fields required whenever that object is validated. For .ca, tldRequirements.legal_type and tldRequirements.lang_pref are required, and contacts.registrant is itself required under contacts.

The properties object defines fields and their constraints. A field existing here does not necessarily make it required; for example, companyName is defined under contacts.registrant.properties but is not part of the base registrant.required array.

For .ca, we express this rule as an allOf entry containing an if/then conditional. The if branch checks whether tldRequirements.legal_type matches one of the specified values. If it does match, the then branch adds companyName to the registrant's required array.

Your application needs to account for both the static required arrays and applicable conditional rules when determining which fields are required for the current form state. A field can become required based on another field's value, which is why we recommend deriving TLD-specific requirements from the schema instead of maintaining a separate lookup table.

Schema-driven form generation

A schema-driven form generator can use the returned schema and current form state to determine which fields to render and which validation rules apply, without hard-coding individual TLD requirements into the form logic.

For .ca, changing legal_type from RES to CCO should trigger a re-evaluation of the applicable requirements. Because CCO is one of the values covered by the schema's conditional rule, companyName becomes required and can be shown as a required field. Changing the value back to one that does not satisfy the condition should remove that conditional requirement.

The following example is pseudocode rather than a complete schema-to-form implementation. It illustrates the basic flow: read the schema's properties and required fields, evaluate applicable conditional rules against the current form state, and use the result to build the fields for the UI. A production implementation would need to recursively handle nested objects such as contacts.registrant and conditionals nested inside allOf.

TypeScript
type FormField = {
  path: string
  required: boolean
  schema: Record<string, unknown>
}

function getApplicableFields(
  schema: Record<string, unknown>,
  formValues: Record<string, unknown>
): FormField[] {
  // Pseudocode: collect the schema's base requirements
  // and any requirements activated by applicable conditions.
  const fields = getFieldsFromProperties(schema.properties)
  const required = getRequiredFields(schema)

  // Evaluate applicable if/then rules against the current form state.
  // A real implementation should traverse allOf and nested schemas.
  for (const rule of getConditionalRules(schema)) {
    if (conditionMatches(rule.if, formValues)) {
      addRequiredFields(required, rule.then)
    }
  }

  return fields.map((field) => ({
    ...field,
    required: required.has(field.path)
  }))
}

The important part is the logic rather than this helper function itself: fetch the schema for the selected TLD, combine its base requirements with any conditional requirements that apply to the current form state, and use that result to drive the UI. Your implementation should also re-evaluate the applicable rules whenever a value referenced by a conditional changes.

Nested paths such as contacts.registrant.companyName require the implementation to traverse the schema rather than treating every property as a top-level field. Likewise, conditional rules can appear inside constructs such as allOf, so a production evaluator should not assume that an if/then pair exists directly on the root schema.

Local validation of registrant data

Before you POST to /core/v1/domains, validate the assembled registration payload locally against the requirementsV2 schema. AJV supports JSON Schema Draft-07 and can run in Node.js or in the browser.

The following example is illustrative TypeScript rather than a complete validation example. It shows the basic AJV flow and converts AJV's error objects into a simpler structure for your application to consume.

TypeScript
import Ajv from 'ajv'
import addFormats from 'ajv-formats'

const ajv = new Ajv({ allErrors: true })
addFormats(ajv)

function validateRegistrantPayload(
  schema: Record<string, unknown>,
  payload: Record<string, unknown>
): {
  valid: boolean
  errors: Array<{ path: string; message: string }>
} {
  const validate = ajv.compile(schema)
  const valid = validate(payload)

  if (valid) {
    return { valid: true, errors: [] }
  }

  const errors = (validate.errors ?? []).map((err) => ({
    path: err.instancePath || '',
    message: err.message ?? 'Invalid value'
  }))

  return { valid: false, errors }
}

For .ca, local validation can catch several categories of predictable schema-level error before the request reaches the API:

  • Missing required fields: if legal_type is CCO but companyName is absent, the schema's if/then condition makes companyName required and AJV reports the missing property.
  • Invalid allowed values: a legal_type value that does not match one of the values permitted by the schema fails validation.
  • Pattern violations: the .ca postal-code pattern is ^[A-Za-z]\d[A-Za-z] \d[A-Za-z]\d$. A value such as "90210" fails the pattern constraint.

Local validation covers predictable errors expressed by the JSON Schema, but API-side validation remains authoritative. The API or registry may also apply requirements that are not represented by the schema, such as eligibility or verification rules. Plan for both validation paths.

How to map API errors to actionable user feedback

When the API rejects a registration request with a 400, the response's message field can contain one or more validation errors, including the path of the field that caused each error. For example, a failed .it registration can return errors such as domain.contacts.registrant.firstName and tldRequirements.X-IT-PIN, giving your application enough information to map individual errors back to the appropriate form fields.

Again, this example is illustrative pseudocode rather than a complete error parser. The exact parsing logic depends on how your application represents schema paths and form fields, but the basic approach is to extract each field path from the API's validation message and associate it with the corresponding UI field.

TypeScript
type FieldError = {
  field: string | null
  message: string
}

function mapApiErrors(message: string): FieldError[] {
  // Pseudocode: split the API's combined validation message
  // and extract the field path from each error.
  const errors = message.split('; ')

  return errors.map((error) => {
    const separator = error.indexOf(': ')
    
    if (separator === -1) {
      return {
        field: null,
        message: error
      }
    }

    const field = error.slice(0, separator)
    const message = error.slice(separator + 2)

    return {
      field,
      message
    }
  })
}

By performing local schema validation you can catch many predictable errors before the request is made, while API-side validation handles errors that only become apparent when the request reaches the server. A field-level mapping can make those responses easier to act on, but the application should retain a form-level fallback for errors that cannot be mapped reliably.

Frequently asked questions

What does the name.com ccTLD registration requirements API return?

The requirementsV2 endpoint returns a JSON Schema Draft-07 document describing the fields and validation rules exposed by name.com for a given TLD, including conditional requirements where applicable.

How do I validate ccTLD registrant data before submitting a registration request?

Run the registration payload through a JSON Schema validator such as AJV against the requirementsV2 schema before POSTing; this catches schema-level errors such as missing required fields, invalid values, format violations, and pattern failures.

What causes a 400 error when registering a ccTLD domain?

A 400 can result from invalid or incomplete registration data, among other request-validation problems; inspect the response’s message and any available details to provide more useful feedback than a generic failure message.

Can local ajv validation replace API-side validation entirely?

No. Local validation catches predictable errors expressed by the JSON Schema, but API-side validation remains authoritative and can reject requests for requirements that local schema validation cannot determine.

Putting it all together

The full pattern runs in four steps. Fetch the requirementsV2 schema for the selected TLD. Use its required arrays and applicable conditional rules to determine which fields the current form needs. Validate the assembled payload with AJV before submission to catch predictable schema-level errors early. If the API rejects the request, use the field paths in its validation response to provide actionable feedback where possible.

The result is a registration flow that moves TLD-specific validation logic out of hard-coded application rules and into the schema our API returns. When the requirements exposed by a TLD's schema change, your application can consume the updated schema rather than requiring a separate hard-coded requirements list.

For implementation details and the current API surface, see the name.com API documentation.