Formato Aberto de Inquérito

Um formato aberto, baseado em JSON, para definir inquéritos e formulários. Usado por YourOpinion.is e de implementação livre para qualquer pessoa.

Versão atual: 1.0 (Publicado: 2025-11-15)

Descarregar Especificação

  • Esquema JSON - Definição de formato legível por máquina
  • Markdown - Documentação legível por humanos (otimizada para LLMs)

Introdução

O Formato Aberto de Inquérito fornece uma maneira padronizada de definir inquéritos, questionários e formulários como objetos JSON. Este formato permite:

  • Portabilidade - Mover inquéritos entre diferentes plataformas e ferramentas
  • Controlo de versões - Acompanhar alterações no inquérito usando ferramentas de diff padrão
  • Geração programática - Criar inquéritos dinamicamente a partir de código
  • Compatibilidade com LLMs - Assistentes de IA podem criar e modificar inquéritos facilmente

Conceitos Essenciais

Coleções (Páginas)

Os inquéritos são organizados em coleções, que funcionam como páginas ou secções. Cada inquérito deve conter pelo menos uma coleção.

Elementos

Componentes individuais dentro de uma coleção, tais como:

  • Perguntas (entrada de texto, escolha múltipla, classificações, etc.)
  • Blocos de conteúdo (texto em Markdown, imagens)
  • Elementos de controlo de fluxo (lógica condicional, saltos de página)

Recursos reutilizáveis (Assets)

Componentes reutilizáveis que podem ser referenciados por várias perguntas:

  • Listas de opções (para perguntas de seleção)
  • Escalas de classificação
  • Recursos partilhados

Estrutura do Documento

Um inquérito é representado como um objeto JSON com esta estrutura:

{
    "$schema": "https://youropinion.is/json-schema/1.0",
    "$readme": "https://youropinion.is/docs/survey-format.md",
    "collections": {
        "collection-id-1": {
            "name": "Nome da Coleção",
            "elements": {
                "element-id-1": {
                    "type": "Markdown",
                    "data": {
                        /* Dados específicos do elemento */
                    }
                }
            },
            "displayOrder": ["element-id-1"]
        }
    },
    "displayOrder": ["collection-id-1"],
    "assets": {
        "asset-id-1": {
            "type": "options",
            "data": {
                /* Dados específicos do recurso */
            }
        }
    }
}

Propriedades de Nível Superior

$schema (opcional)

URL para a definição do Esquema JSON para validação:

"$schema": "https://youropinion.is/json-schema/1.0"

$readme (opcional)

URL para a documentação legível por humanos:

"$readme": "https://youropinion.is/docs/survey-format.md"

collections (obrigatório)

Objeto que contém as páginas/secções do inquérito. Cada chave é um ID de coleção único:

"collections": {
    "welcome": {
        "name": "Página de Boas-vindas",
        "elements": { /* ... */ },
        "displayOrder": ["intro", "consent"]
    },
    "questions": {
        "name": "Perguntas Principais",
        "elements": { /* ... */ },
        "displayOrder": ["q1", "q2", "q3"]
    }
}

Propriedades da Coleção:

  • name (string, opcional) - Nome da coleção legível por humanos (não exibido aos utilizadores, usado para referência)
  • condition (objeto, opcional) - Lógica condicional para determinar se esta coleção deve ser mostrada (ver secção Lógica Condicional)
  • elements (objeto) - Contém os elementos para esta coleção. Cada chave é um ID de elemento único
  • displayOrder (array) - Array de IDs de elementos que define a ordem de exibição

Importante: O array displayOrder atua como filtro e sequenciador:

  • Apenas os elementos listados em displayOrder serão mostrados
  • Elementos que não estão em displayOrder permanecem na definição, mas ficam ocultos
  • Incluir um ID de elemento inexistente causará um erro

displayOrder (obrigatório)

Array que especifica a ordem das coleções:

"displayOrder": ["welcome", "questions", "thank-you"]

As mesmas regras de filtragem se aplicam: apenas as coleções listadas aqui serão mostradas.

assets (opcional)

Componentes reutilizáveis referenciados por múltiplos elementos. Os recursos reutilizáveis (assets) reduzem a duplicação e facilitam a manutenção dos inquéritos.

Tipos de Recursos (Assets):

  1. options - Listas de opções reutilizáveis para perguntas SelectOne/SelectMany
  2. ordinal-scale - Escalas rotuladas reutilizáveis para perguntas OrdinalScale
  3. interval-scale - Escalas numéricas reutilizáveis para perguntas IntervalScale

Exemplo - Recurso de opções:

"assets": {
    "color-options": {
        "type": "options",
        "name": "Opções de Cores",
        "data": {
            "options": {
                "red": { "label": "Vermelho" },
                "blue": { "label": "Azul" },
                "green": { "label": "Verde" },
                "yellow": { "label": "Amarelo" }
            },
            "displayOrder": ["red", "blue", "green", "yellow"]
        }
    },
    "satisfaction-scale": {
        "type": "ordinal-scale",
        "name": "Escala de Satisfação Padrão",
        "data": {
            "labels": {
                "1": "Muito Insatisfeito",
                "2": "Insatisfeito",
                "3": "Neutro",
                "4": "Satisfeito",
                "5": "Muito Satisfeito"
            }
        }
    },
    "nps-scale": {
        "type": "interval-scale",
        "name": "Escala Net Promoter Score (NPS)",
        "data": {
            "start": 0,
            "end": 10,
            "labels": {
                "start": "Pouco provável",
                "end": "Muito provável"
            }
        }
    }
}

Todos os tipos de recursos exigem:

  • type - Tipo de recurso: "options", "ordinal-scale", ou "interval-scale"
  • name - Nome legível por humanos para fins de documentação
  • data - Estrutura de dados específica do tipo

Os recursos podem ser referenciados usando referências JSON (ver secção Referências JSON).


Tipos de Elementos

Os elementos são os blocos de construção do seu inquérito. Cada elemento tem uma propriedade type e data. Todos os elementos podem opcionalmente incluir um objeto extensions para metadados personalizados (não usado pelo renderizador padrão).

Tipos de elementos disponíveis:

TipoCategoriaDescrição
MarkdownConteúdoExibir texto e conteúdo formatado
FlowControlControloLógica condicional e navegação
StringPerguntaEntrada de texto (linha única ou múltipla)
NumberPerguntaEntrada numérica
DatePerguntaSeletor de data
BooleanPerguntaCaixa de seleção Sim/Não
SelectOnePerguntaEscolha única de opções (botões de rádio)
SelectManyPerguntaMúltiplas escolhas de opções (caixas de seleção)
IntervalScalePerguntaEscala de classificação numérica (ex: 0-10 para NPS, 1-5 para satisfação)
OrdinalScalePerguntaEscala de classificação rotulada (ex: Discordo Totalmente a Concordo Totalmente)
PaymentPerguntaElemento de processamento de pagamento

Elementos de Conteúdo

Markdown

Exibe texto formatado, títulos, listas e outro conteúdo usando a sintaxe Markdown.

{
    "type": "Markdown",
    "data": {
        "markdown": "# Bem-vindo(a)!\n\nObrigado por participar no nosso inquérito.\n\n- Por favor, responda honestamente\n- Todas as respostas são anónimas"
    }
}

Markdown Suportado:

  • Títulos (#, ##, ###)
  • Listas (ordenadas e não ordenadas)
  • Negrito (**texto**) e itálico (*texto*)
  • Links ([texto](url))
  • Blocos de código

FlowControl

Controla o fluxo do inquérito com lógica condicional e navegação.

{
    "type": "FlowControl",
    "data": {
        "condition": {
            /* Opcional - Ver secção Lógica Condicional */
        },
        "action": {
            "type": "survey-finish" // Opções: "survey-finish" ou "page-finish"
        }
    }
}

Tipos de Ação:

  • survey-finish - Conclui o inquérito imediatamente
  • page-finish - Termina a página atual e avança para a próxima

Para informações detalhadas sobre a estrutura da condição e exemplos, consulte a secção Lógica Condicional abaixo.


Tipos de Perguntas

Todas as perguntas partilham estas propriedades comuns:

{
    "type": "QuestionType",
    "data": {
        "label": "O texto da sua pergunta aqui",
        "required": "yes", // Opcional: "yes", "no", ou "suggested" (o padrão é "no")
        "markdown": "Texto de ajuda" // Opcional: contexto adicional em Markdown
    }
}

Propriedades comuns:

  • label (string, obrigatório) - O texto da pergunta mostrado aos utilizadores
  • required (string, opcional) - Se a resposta é obrigatória: "yes", "no", ou "suggested" (mostra como opcional, mas recomendado)
  • markdown (string, opcional) - Texto de ajuda adicional ou descrição em formato Markdown
  • defaultValue (any, opcional) - Valor pré-preenchido para a pergunta

String

Entrada de texto de linha única ou múltipla.

{
    "type": "String",
    "data": {
        "label": "Qual é o seu nome?",
        "placeholder": "Insira o seu nome completo", // Opcional
        "multiline": false, // Opcional: true para área de texto
        "required": "yes"
    }
}

Number

Entrada numérica com validação opcional.

{
    "type": "Number",
    "data": {
        "label": "Quantos funcionários?",
        "min": 1, // Opcional: valor mínimo
        "max": 1000, // Opcional: valor máximo
        "step": 1, // Opcional: tamanho do incremento (padrão: 1)
        "required": "yes"
    }
}

Propriedades:

  • min (number, opcional) - Valor mínimo permitido
  • max (number, opcional) - Valor máximo permitido
  • step (number, opcional) - Tamanho do incremento para validação da entrada (padrão: 1)

Date

Entrada de seletor de data com nível de precisão opcional e restrições de min/max.

{
    "type": "Date",
    "data": {
        "label": "Quando se juntou a nós?",
        "required": "suggested",
        "accuracy": "day", // Opcional: "day", "month", ou "year" (padrão: "day")
        "min": "2020-01-01", // Opcional: data mais antiga selecionável
        "max": "2025-12-31" // Opcional: data mais recente selecionável
    }
}

Propriedades:

  • accuracy (string, opcional) - Nível de precisão da data:
    • "day" - Seletor de data completo com calendário (padrão)
    • "month" - Seleção de mês e ano em menu suspenso
    • "year" - Seleção apenas do ano em menu suspenso
  • min (string, opcional) - Data mais antiga selecionável. Pode ser:
    • String de data ISO (ex: "2020-01-01")
    • String de tempo relativo (ex: "+ 3 months", "now", "- 1 year")
  • max (string, opcional) - Data mais recente selecionável. Mesmas opções de formato que min

Boolean

Caixa de seleção Sim/Não. O valor padrão é false (não marcada).

{
    "type": "Boolean",
    "data": {
        "label": "Termos e Condições",
        "description": "Concordo com os termos e condições" // Mostrado ao lado da caixa de seleção
    }
}

SelectOne

Pergunta de escolha múltipla (botões de rádio) - os utilizadores selecionam uma opção.

{
    "type": "SelectOne",
    "data": {
        "label": "Qual é a sua cor favorita?",
        "required": "yes",
        "options": {
            "options": {
                "red": { "label": "Vermelho" },
                "blue": { "label": "Azul" },
                "green": { "label": "Verde" }
            },
            "displayOrder": ["red", "blue", "green"]
        }
    }
}

Usando referências de recursos:

{
    "type": "SelectOne",
    "data": {
        "label": "Escolha uma cor",
        "options": { "$ref": "#/assets/color-options/data" }
    }
}

SelectMany

Pergunta de escolha múltipla (caixas de seleção) - os utilizadores podem selecionar várias opções.

{
    "type": "SelectMany",
    "data": {
        "label": "Que sistemas operativos utiliza?",
        "required": "yes",
        "options": {
            "options": {
                "windows": { "label": "Windows" },
                "macos": { "label": "macOS" },
                "linux": { "label": "Linux" }
            },
            "displayOrder": ["windows", "macos", "linux"]
        },
        "other": true, // Opcional: adiciona a opção "Outro" com um campo de texto
        "minSelections": 1, // Opcional: número mínimo de seleções obrigatórias
        "maxSelections": 3 // Opcional: número máximo de seleções permitidas
    }
}

IntervalScale

Escala de classificação numérica com um intervalo definido (ex: 0-10 para NPS, 1-5 para satisfação). Comumente usada para:

  • Net Promoter Score (NPS): escala de 0-10
  • Classificações de satisfação: escala de 1-5 ou 1-7
  • Perguntas de probabilidade: escala de 0-10
{
    "type": "IntervalScale",
    "data": {
        "label": "Qual a probabilidade de nos recomendar a um amigo?",
        "required": "yes",
        "scale": {
            "start": 0, // Número inicial da escala
            "end": 10, // Número final da escala
            "labels": {
                // Rótulos para os pontos inicial e final (obrigatório)
                "start": "Nada provável",
                "end": "Extremamente provável"
            }
        }
    }
}

Casos de uso comuns:

// Net Promoter Score (NPS)
{
    "type": "IntervalScale",
    "data": {
        "label": "Qual a probabilidade de nos recomendar?",
        "scale": {
            "start": 0,
            "end": 10,
            "labels": {
                "start": "Pouco provável",
                "end": "Muito provável"
            }
        }
    }
}

// Escala de satisfação de 5 pontos
{
    "type": "IntervalScale",
    "data": {
        "label": "Qual o seu grau de satisfação com o nosso serviço?",
        "scale": {
            "start": 1,
            "end": 5,
            "labels": {
                "start": "Muito insatisfeito",
                "end": "Muito satisfeito"
            }
        }
    }
}

Exibição: Mostra uma escala horizontal com números clicáveis. Os rótulos aparecem abaixo dos pontos inicial e final.

OrdinalScale

Escala de classificação rotulada onde cada ponto tem um rótulo personalizado. Ideal para:

  • Escalas de Likert com formulações específicas para cada ponto
  • Escalas de concordância (Discordo Totalmente → Concordo Totalmente)
  • Escalas de frequência (Nunca → Sempre)
  • Escalas de classificação personalizadas com rótulos significativos
{
    "type": "OrdinalScale",
    "data": {
        "label": "Qual o seu grau de satisfação com a sua posição?",
        "required": "yes",
        "scale": {
            "labels": {
                "1": "Muito Insatisfeito",
                "2": "Insatisfeito",
                "3": "Neutro",
                "4": "Satisfeito",
                "5": "Muito Satisfeito"
            }
        }
    }
}

Casos de uso comuns:

// Escala de concordância de Likert
{
    "type": "OrdinalScale",
    "data": {
        "label": "O produto correspondeu às minhas expectativas.",
        "scale": {
            "labels": {
                "1": "Discordo Totalmente",
                "2": "Discordo",
                "3": "Neutro",
                "4": "Concordo",
                "5": "Concordo Totalmente"
            }
        }
    }
}

// Escala de frequência
{
    "type": "OrdinalScale",
    "data": {
        "label": "Com que frequência utiliza o nosso produto?",
        "scale": {
            "labels": {
                "1": "Nunca",
                "2": "Raramente",
                "3": "Às vezes",
                "4": "Frequentemente",
                "5": "Sempre"
            }
        }
    }
}

Exibição: Mostra botões ou opções rotuladas. Cada rótulo é exibido por completo, tornando a escala autoexplicativa.

IntervalScale vs OrdinalScale:

  • Use IntervalScale quando os próprios números têm significado (0-10, 1-5)
  • Use OrdinalScale quando precisa de rótulos personalizados para cada ponto
  • IntervalScale é mais compacto; OrdinalScale é mais descritivo

Payment

Recolhe informações de pagamento e processa transações. Integra-se com os fornecedores de pagamento configurados nas definições do seu inquérito.

{
    "type": "Payment",
    "data": {
        "label": "Pagamento",
        "required": "yes",
        "amount": {
            "value": 29.99,
            "currency": "USD"
        },
        "captureMethod": "automatic" // Opcional: "immediate", "manual", ou "automatic"
    }
}

Propriedades:

  • amount (objeto, obrigatório) - Valor do pagamento com value (número) e currency (código de 3 letras, ex: “USD”, “EUR”, “GBP”)
  • captureMethod (string, opcional) - Quando capturar o pagamento:
    • "immediate" - Captura o pagamento imediatamente quando o inquérito é submetido
    • "manual" - Requer captura manual através do seu painel de pagamentos
    • "automatic" - Captura automaticamente quando o inquérito é concluído (padrão)

Nota: O processamento de pagamentos requer que um fornecedor de pagamentos esteja configurado nas definições do seu canal. As moedas e métodos de pagamento suportados dependem da configuração do seu fornecedor.


Referências JSON

Reduza a duplicação referenciando componentes reutilizáveis. As referências usam este formato:

{ "$ref": "[<localização>]#<caminho>" }
  • localização - URL para o documento de origem (vazio = documento atual)
  • caminho - Caminho a partir da raiz do documento usando separadores /

Exemplo - Duplicação em linha:

{
    "question-1": {
        "type": "SelectOne",
        "data": {
            "label": "Cor favorita?",
            "options": {
                "options": {
                    "red": { "label": "Vermelho" },
                    "blue": { "label": "Azul" }
                },
                "displayOrder": ["red", "blue"]
            }
        }
    },
    "question-2": {
        "type": "SelectOne",
        "data": {
            "label": "Cor de que menos gosta?",
            "options": {
                "options": {
                    "red": { "label": "Vermelho" },
                    "blue": { "label": "Azul" }
                },
                "displayOrder": ["red", "blue"]
            }
        }
    }
}

Melhor - Usando recursos (assets):

{
    "collections": {
        "main": {
            "elements": {
                "question-1": {
                    "type": "SelectOne",
                    "data": {
                        "label": "Cor favorita?",
                        "options": { "$ref": "#/assets/colors/data" }
                    }
                },
                "question-2": {
                    "type": "SelectOne",
                    "data": {
                        "label": "Cor de que menos gosta?",
                        "options": { "$ref": "#/assets/colors/data" }
                    }
                }
            },
            "displayOrder": ["question-1", "question-2"]
        }
    },
    "assets": {
        "colors": {
            "type": "options",
            "data": {
                "options": {
                    "red": { "label": "Vermelho" },
                    "blue": { "label": "Azul" }
                },
                "displayOrder": ["red", "blue"]
            }
        }
    }
}

Benefícios:

  • Fonte única de verdade
  • Manutenção mais fácil
  • Diferenças (diffs) de controlo de versões mais claras
  • Tamanho de ficheiro menor

Lógica Condicional

A lógica condicional permite criar inquéritos dinâmicos que se adaptam com base nas respostas do utilizador. As condições podem ser usadas de duas maneiras:

1. Condições ao Nível da Coleção

Mostra ou oculta páginas inteiras com base em condições. Se a condição de uma coleção for avaliada como false, a página inteira é ignorada e a execução passa para a próxima coleção.

{
    "collections": {
        "follow-up": {
            "name": "Perguntas de Acompanhamento",
            "condition": {
                "type": "condition",
                "fact": "questions/nps-score",
                "operator": "lt",
                "compare": { "value": 7 }
            },
            "elements": {
                /* ... */
            },
            "displayOrder": ["question-1"]
        }
    }
}

Neste exemplo, a página “Perguntas de Acompanhamento” só é mostrada se a pontuação NPS for inferior a 7.

2. Elementos FlowControl

Controla o fluxo do inquérito dentro de uma página usando elementos FlowControl. Quando a condição é avaliada como true, a ação especificada é executada.

Ações disponíveis:

  • survey-finish - Conclui o inquérito imediatamente (ignora todas as páginas restantes)
  • page-finish - Termina a página atual e avança para a próxima (ignora os elementos restantes na página atual)
{
    "type": "FlowControl",
    "data": {
        "condition": {
            "type": "condition",
            "fact": "satisfaction",
            "operator": "eq",
            "compare": { "value": "very-satisfied" }
        },
        "action": {
            "type": "page-finish"
        }
    }
}

Nota: Se nenhuma condition for fornecida, a ação é sempre executada quando o elemento é alcançado.

Estrutura da Condição

As condições são definidas como uma árvore binária com dois tipos de nós:

1. Condições de Comparação

Compara um facto (resposta do inquérito) com um valor ou outro facto.

{
    "type": "condition",
    "fact": "questions/age",
    "operator": "gt",
    "compare": { "value": 18 }
}

Propriedades:

  • type - Sempre "condition" para comparações
  • fact (string) - A referência do elemento no formato ‘collection-id/element-id’ de onde ler o valor
  • operator (string) - Operador de comparação (ver operadores abaixo)
  • compare (objeto, opcional) - Com o que comparar:
    • { "value": <any> } - Compara com um valor literal
    • { "fact": "<collection-id/element-id>" } - Compara com o valor de outro elemento
    • Não é necessário para operadores como "exists" ou "true"
  • not (boolean, opcional) - Inverte o resultado (padrão: false)

Operadores suportados por tipo de elemento:

Number, IntervalScale, OrdinalScale, Date:

  • "eq" - Igual a
  • "gt" - Maior que
  • "gte" - Maior ou igual a
  • "lt" - Menor que
  • "lte" - Menor ou igual a
  • "exists" - Tem um valor (não precisa de compare)

String:

  • "eq" - Igual a
  • "contains" - A string contém o valor
  • "exists" - Tem um valor (não precisa de compare)

Boolean:

  • "true" - O valor é verdadeiro (não precisa de compare)
  • "exists" - Tem um valor (não precisa de compare)

SelectOne:

  • "eq" - Igual a uma opção específica
  • "in" - É uma de várias opções (o valor de comparação deve estar no formato SelectMany)
  • "exists" - Tem um valor (não precisa de compare)

SelectMany:

  • "eq" - Corresponde exatamente a um conjunto de opções
  • "exists" - Tem pelo menos um valor (não precisa de compare)

2. Condições em Cadeia

Combina múltiplas condições usando operadores lógicos.

{
    "type": "all",
    "items": [
        {
            "type": "condition",
            "fact": "questions/age",
            "operator": "gt",
            "compare": { "value": 18 }
        },
        {
            "type": "condition",
            "fact": "questions/country",
            "operator": "eq",
            "compare": { "value": "US" }
        }
    ]
}

Propriedades:

  • type - Operador lógico:
    • "all" - Todas as condições devem ser verdadeiras (lógica E)
    • "any" - Pelo menos uma condição deve ser verdadeira (lógica OU)
  • items (array) - Array de condições (podem ser comparações ou cadeias aninhadas)
  • not (boolean, opcional) - Inverte o resultado de toda a cadeia
  • name (string, opcional) - Rótulo legível por humanos para documentação

Exemplos

Ignorar inquérito para clientes satisfeitos

{
    "type": "FlowControl",
    "data": {
        "condition": {
            "type": "condition",
            "fact": "questions/nps",
            "operator": "gte",
            "compare": { "value": 9 }
        },
        "action": {
            "type": "survey-finish"
        }
    }
}

Mostrar página apenas para uma faixa etária específica

{
    "collections": {
        "teen-questions": {
            "name": "Perguntas para Adolescentes",
            "condition": {
                "type": "all",
                "items": [
                    {
                        "type": "condition",
                        "fact": "questions/age",
                        "operator": "gte",
                        "compare": { "value": 13 }
                    },
                    {
                        "type": "condition",
                        "fact": "questions/age",
                        "operator": "lt",
                        "compare": { "value": 20 }
                    }
                ]
            },
            "elements": {
                /* ... */
            }
        }
    }
}

Condições aninhadas complexas

{
    "type": "any",
    "name": "Utilizadores Premium ou que gastam muito",
    "items": [
        {
            "type": "condition",
            "fact": "questions/membership",
            "operator": "eq",
            "compare": { "value": "premium" }
        },
        {
            "type": "all",
            "items": [
                {
                    "type": "condition",
                    "fact": "questions/total-spent",
                    "operator": "gt",
                    "compare": { "value": 1000 }
                },
                {
                    "type": "condition",
                    "fact": "questions/active-months",
                    "operator": "gte",
                    "compare": { "value": 6 }
                }
            ]
        }
    ]
}

Comparar dois factos

{
    "type": "condition",
    "fact": "questions/current-salary",
    "operator": "gt",
    "compare": { "fact": "questions/desired-salary" }
}

Usar a propriedade not

{
    "type": "condition",
    "fact": "questions/email-consent",
    "operator": "true",
    "not": true // Inverte o resultado: verdadeiro quando email-consent NÃO é verdadeiro
}

Histórico de Versões

VersãoDataAlterações
1.02025-11-15Elemento de Pagamento
0.52025-01-24Lançamento público inicial