Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/gcsconsultores/gcs-website/llms.txt

Use this file to discover all available pages before exploring further.

submitLead() is a Next.js Server Action defined in app/actions/lead-actions.ts that validates and processes contact form submissions for GCS’s free Orientación Estratégica offering. It runs exclusively on the server, enforces the same Zod schema used by the browser form (defence-in-depth), and delegates CRM delivery to the sendToCrm adapter — keeping the rest of the application decoupled from any particular CRM provider.

Return type

Every call to submitLead() resolves to a LeadResult discriminated union. The client reads the ok flag to decide whether to show a success message or inline field errors.
export type LeadResult =
  | { ok: true; message: string }
  | { ok: false; message: string; fieldErrors?: Record<string, string> }
fieldErrors is a flat object mapping each invalid field name to a Spanish-language error string ready to display next to the form control.

Function signature

export async function submitLead(raw: unknown): Promise<LeadResult>
The parameter is typed as unknown deliberately — the function never trusts the caller. All type safety is established by leadSchema.safeParse(raw) at runtime.

Processing flow

1

Validate input with leadSchema.safeParse

The raw value is parsed against leadSchema (defined in lib/schemas.ts). Because the schema is shared between the client form and this server action, any mutation of the payload in transit is caught immediately.
2

Return field errors if validation fails

If parsed.success is false, the action iterates over parsed.error.issues to build a fieldErrors map and returns early without touching the CRM.
return {
  ok: false,
  message: 'Revise los campos marcados e inténtelo de nuevo.',
  fieldErrors,
}
3

Call sendToCrm with the validated lead

The validated parsed.data (typed as LeadInput) is passed to sendToCrm(). This is the single integration point — all CRM-specific logic lives inside that function.
4

Return ok: false if the CRM call throws

If sendToCrm rejects (network error, non-2xx response, etc.) the error is caught and a user-facing Spanish message is returned without exposing internal details.
return {
  ok: false,
  message:
    'Ocurrió un problema al enviar su solicitud. Por favor intente de nuevo en unos minutos.',
}
5

Return ok: true on success

When the CRM call resolves without error, the action returns a success result with a confirmation message.
return {
  ok: true,
  message:
    '¡Gracias! Hemos recibido su solicitud. Un consultor de GCS se pondrá en contacto en menos de 24 horas hábiles.',
}

leadSchema field reference

leadSchema is a Zod object defined in lib/schemas.ts. All messages are in Spanish.
name
string
required
Full name of the contact. Minimum 2 characters, maximum 80. Whitespace is trimmed before validation.
company
string
required
Name of the contact’s organisation. Minimum 2 characters, maximum 120. Trimmed before validation.
email
string
required
Valid email address. Validated with Zod’s .email() check after trimming. Minimum length 1 (to produce the correct “required” error rather than an “invalid email” error on an empty field).
phone
string
required
Colombian or international phone number. Must match /^[+]?[\d\s().-]{7,18}$/ — between 7 and 18 characters, allowing digits, spaces, parentheses, dashes, dots, and an optional leading +. Trimmed before validation.
interest
enum
required
Area of interest selected from the dropdown. Must be one of the following values:
ValueLabel displayed in the UI
crecimientoCrecimiento Estratégico
riesgoRiesgo & Cumplimiento
estrategiaEstrategia Ejecutiva
innovacionInnovación & Tecnología
talentoTalento & Capacitación
calidadCalidad & Mejoramiento
otroOtro / No estoy seguro
message
string
Optional free-text message from the contact. Maximum 1000 characters. An empty string is also accepted (coerced to undefined by .or(z.literal(''))).
Must be the boolean literal true. This field cannot be pre-checked or defaulted — it requires an explicit user action.
dataConsent must be explicitly true. The form control must not be pre-checked or set to true by default. This requirement directly implements Colombia’s data protection law — Ley 1581 de 2012 — which mandates a free, prior, and express consent for the processing of personal data.

interestOptions mapping

The interestOptions array exported from lib/schemas.ts is the single source of truth for both the <select> dropdown in the form and the interest enum in the schema. Add or remove entries here to keep both in sync.
export const interestOptions: { value: LeadInput['interest']; label: string }[] = [
  { value: 'crecimiento', label: 'Crecimiento Estratégico' },
  { value: 'riesgo',      label: 'Riesgo & Cumplimiento' },
  { value: 'estrategia',  label: 'Estrategia Ejecutiva' },
  { value: 'innovacion',  label: 'Innovación & Tecnología' },
  { value: 'talento',     label: 'Talento & Capacitación' },
  { value: 'calidad',     label: 'Calidad & Mejoramiento' },
  { value: 'otro',        label: 'Otro / No estoy seguro' },
]

sendToCrm adapter

sendToCrm is a private async function in the same file. It follows the adapter pattern — all CRM-specific details are confined here so that submitLead and the rest of the application never depend on a concrete provider.

Required environment variables

CRM_API_URL
string
The full URL of the CRM’s lead or contact creation endpoint. Leave unset during local development — leads will be logged to the server console instead.
CRM_API_KEY
string
API key or Bearer token for the CRM. Sent as Authorization: Bearer <CRM_API_KEY> on every request.
When CRM_API_URL is not set (or CRM_API_KEY is missing), sendToCrm logs the lead fields to the server console and returns without error. No lead data is lost — you can collect submissions safely before the CRM is connected.

Fetch body structure

The function sends a POST request with the following JSON body. Remap the property names inside sendToCrm to match the field names required by your CRM provider.
{
  "properties": {
    "full_name": "string",
    "company": "string",
    "email": "string",
    "phone": "string",
    "area_of_interest": "crecimiento | riesgo | estrategia | innovacion | talento | calidad | otro",
    "message": "string (empty string when not provided)",
    "source": "Sitio web GCS — Orientación Estratégica",
    "submitted_at": "ISO 8601 datetime"
  }
}

Full schema source

import { z } from 'zod'

export const leadSchema = z.object({
  name: z
    .string()
    .trim()
    .min(2, 'Por favor ingrese su nombre completo.')
    .max(80, 'El nombre es demasiado largo.'),
  company: z
    .string()
    .trim()
    .min(2, 'Indique el nombre de su empresa.')
    .max(120, 'El nombre de la empresa es demasiado largo.'),
  email: z
    .string()
    .trim()
    .min(1, 'El correo es obligatorio.')
    .email('Ingrese un correo electrónico válido.'),
  // Teléfono colombiano / internacional flexible: 7 a 18 caracteres.
  phone: z
    .string()
    .trim()
    .regex(
      /^[+]?[\d\s().-]{7,18}$/,
      'Ingrese un teléfono válido (mínimo 7 dígitos).',
    ),
  interest: z.enum(
    [
      'crecimiento',
      'riesgo',
      'estrategia',
      'innovacion',
      'talento',
      'calidad',
      'otro',
    ],
    { message: 'Seleccione un área de interés.' },
  ),
  message: z
    .string()
    .trim()
    .max(1000, 'El mensaje no puede superar los 1000 caracteres.')
    .optional()
    .or(z.literal('')),
  // Autorización de tratamiento de datos (Ley 1581 de 2012 - Colombia).
  // Debe ser explícitamente true (casilla NO pre-marcada).
  dataConsent: z.literal(true, {
    message: 'Debe autorizar el tratamiento de sus datos para continuar.',
  }),
})

export type LeadInput = z.infer<typeof leadSchema>

Build docs developers (and LLMs) love