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
diffpadrã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 únicodisplayOrder(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
displayOrderserão mostrados - Elementos que não estão em
displayOrderpermanecem 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):
options- Listas de opções reutilizáveis para perguntasSelectOne/SelectManyordinal-scale- Escalas rotuladas reutilizáveis para perguntasOrdinalScaleinterval-scale- Escalas numéricas reutilizáveis para perguntasIntervalScale
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çãodata- 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:
| Tipo | Categoria | Descrição |
|---|---|---|
Markdown | Conteúdo | Exibir texto e conteúdo formatado |
FlowControl | Controlo | Lógica condicional e navegação |
String | Pergunta | Entrada de texto (linha única ou múltipla) |
Number | Pergunta | Entrada numérica |
Date | Pergunta | Seletor de data |
Boolean | Pergunta | Caixa de seleção Sim/Não |
SelectOne | Pergunta | Escolha única de opções (botões de rádio) |
SelectMany | Pergunta | Múltiplas escolhas de opções (caixas de seleção) |
IntervalScale | Pergunta | Escala de classificação numérica (ex: 0-10 para NPS, 1-5 para satisfação) |
OrdinalScale | Pergunta | Escala de classificação rotulada (ex: Discordo Totalmente a Concordo Totalmente) |
Payment | Pergunta | Elemento 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 imediatamentepage-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 utilizadoresrequired(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 MarkdowndefaultValue(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 permitidomax(number, opcional) - Valor máximo permitidostep(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")
- String de data ISO (ex:
max(string, opcional) - Data mais recente selecionável. Mesmas opções de formato quemin
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 comvalue(número) ecurrency(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çõesfact(string) - A referência do elemento no formato ‘collection-id/element-id’ de onde ler o valoroperator(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 decompare)
String:
"eq"- Igual a"contains"- A string contém o valor"exists"- Tem um valor (não precisa decompare)
Boolean:
"true"- O valor é verdadeiro (não precisa decompare)"exists"- Tem um valor (não precisa decompare)
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 decompare)
SelectMany:
"eq"- Corresponde exatamente a um conjunto de opções"exists"- Tem pelo menos um valor (não precisa decompare)
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 cadeianame(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ão | Data | Alterações |
|---|---|---|
| 1.0 | 2025-11-15 | Elemento de Pagamento |
| 0.5 | 2025-01-24 | Lançamento público inicial |