Acompanhar o status da oferta em tempo real

Esse fluxo é destinado à integração do processo de Carta Oferta do R&S Gupy com sistemas externos da sua empresa permitindo receber notificações em tempo real sobre criação e mudanças de status da oferta.

A partir desse envio é possível acompanhar quando uma oferta é criada, enviada ao candidato, visualizada, aceita, recusada ou cancelada, e reagir a esses eventos no seu agente integrador.

📘

Disponibilidade por plano

A funcionalidade de Carta Oferta está incluída em todos os planos com acesso a API, com exceção dos planos Essential e Foundation. A liberação foi feita para 100% da base, exceto clientes nesses planos que não possuem a funcionalidade no plano contratado.

📘

Esse fluxo utiliza webhook

Para realizar integrações utilizando Webhooks é necessário um agente integrador (middleware) para receber e tratar os dados.

Para implementar integrações a partir de webhooks é necessário uma configuração prévia através da própria API Gupy: Veja aqui mais informações sobre o recebimento de webhooks

❗️

Atenção!

A URL usada para receber o webhook DEVE ser um endereço HTTPS válido, exposto publicamente. Para configurar o webhook, consulte Webhook Configuração .

URLs com alta taxa de erro (100% dos erros nos últimos 7 dias) serão removidas sem aviso prévio.

O Webhook espera uma resposta em 30.000 ms. Caso a resposta não tenha ocorrido antes deste tempo, consideramos um timeout, consequentemente, um erro.

O sistema garante pelo menos uma entrega, então podem haver várias notificações do mesmo evento, use a propriedade id para identificar duplicatas.

Não há garantia de ordem de entrega, use a propriedade date para verificar qual evento aconteceu primeiro e classifique os eventos.

NÃO USE serviços como requestcatcher, eles podem expor dados.

É recomendado que o cliente informe em clientHeaders todos os headers que o endpoint de destino espera receber, incluindo o Content-Type correspondente ao seu formato de payload. Por exemplo, se o endpoint espera Content-Type: application/json, esse valor deve ser passado explicitamente em clientHeaders.

Gerando o token

Para utilizar este fluxo, é necessário utilizar o Bearer Token gerado nas configurações avançadas da plataforma. Acesse nossa seção de autenticação para saber como gerar o o token de autenticação.

No momento de gerar o token, habilite os endpoints necessários da V1:

Fluxo de integração

Configurar webhook: evento job-offer.status-changed

  • Acessar endpoint POST /api/v1/webhooks
  • Ajustar o parâmetro action para job-offer.status-changed
  • Inserir no postbackUrl o endereço (URL segura) para onde será direcionado o webhook
  • Inserir o Bearer token e executar a requisição

Exemplo de requisição

curl --request POST \
     --url https://api.gupy.io/api/v1/webhooks \
     --header 'accept: application/json' \
     --header 'authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \
     --header 'content-type: application/json' \
     --data '
{
     "action": "job-offer.status-changed",
     "status": "active",
     "postbackUrl": "https://urldo.agenteintegrador.net",
     "techOwnerName": "Nome Sobrenome",
     "techOwnerEmail": "[email protected]"
}
'

Exemplo de resposta para um webhook cadastrado com sucesso

{
  "id": "d5b2eca6-09c5-4014-9eb8-1d729dd2e3d6",
  "action": "job-offer.status-changed",
  "postbackUrl": "https://urldo.agenteintegrador.net",
  "status": "active"
}

Configurar webhook: evento job-offer.created

  • Acessar endpoint POST /api/v1/webhooks
  • Ajustar o parâmetro action para job-offer.created
  • Inserir no postbackUrl o endereço (URL segura) para onde será direcionado o webhook
  • Inserir o Bearer token e executar a requisição

Exemplo de requisição

curl --request POST \
     --url https://api.gupy.io/api/v1/webhooks \
     --header 'accept: application/json' \
     --header 'authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \
     --header 'content-type: application/json' \
     --data '
{
     "action": "job-offer.created",
     "status": "active",
     "postbackUrl": "https://urldo.agenteintegrador.net",
     "techOwnerName": "Nome Sobrenome",
     "techOwnerEmail": "[email protected]"
}
'

Exemplo de resposta para um webhook cadastrado com sucesso

{
  "id": "d5b2eca6-09c5-4014-9eb8-1d729dd2e3d6",
  "action": "job-offer.created",
  "postbackUrl": "https://urldo.agenteintegrador.net",
  "status": "active"
}

Recebimento no agente integrador

Ciclo de vida da carta oferta

StatusDescrição
notSentCriada, não enviada
sentEnviada ao candidato
viewedVisualizada pelo candidato
acceptedAceita
rejectedRecusada (rejectReason obrigatório)
canceledCancelada (manual ou ao enviar nova oferta)

Evento job-offer.created

O evento será disparado quando uma carta oferta for criada para uma candidatura. Por exemplo: quando um recrutador cria uma oferta com salário e dados de admissão para um candidato.

O payload inclui os dados da oferta, o usuário que a criou e os identificadores da candidatura e da vaga relacionadas.

❗️

Atenção!

A transição para o status notSent não dispara o webhook job-offer.status-changed. Para ser notificado da criação da oferta, configure o evento job-offer.created.

O agente integrador receberá os dados do webhook conforme contrato JSON do evento job-offer.created.

❗️

Atenção!

O exemplo abaixo trata-se de um webhook modelo e não é indicado trabalhar o desenvolvimento a partir do mesmo. O ideal é que o desenvolvimento seja a partir de um payload real.

Exemplo de payload — job-offer.created

{
    "companyName": "Gupy Staging",
    "event": "job-offer.created",
    "id": "52528023-842b-4a54-a254-a5e558d562b0",
    "date": "2026-07-15T17:19:34.135Z",
    "data": {
        "jobOffer": {
            "id": 278167,
            "sentAt": null,
            "status": "notSent",
            "createdAt": "2026-07-15T17:19:34.135Z",
            "updatedAt": "2026-07-15T17:19:34.135Z",
            "answeredAt": null,
            "rejectReason": null,
            "salaryAmount": 10000,
            "admissionDate": "2026-07-15T03:00:00.000Z",
            "admissionType": "employee_admission",
            "salaryCurrency": "R$"
        },
        "user": {
            "id": 48000,
            "name": "User",
            "email": "[email protected]",
            "code": null
        },
        "application": {
            "id": 2937132
        },
        "job": {
            "id": 19139
        }
    }
}

Evento job-offer.status-changed

O evento será disparado quando o status de uma carta oferta mudar. Por exemplo: quando a oferta é enviada ao candidato, visualizada, aceita, recusada ou cancelada.

O payload inclui os dados atuais da oferta (com o status atualizado), os identificadores da candidatura e da vaga, e — quando aplicável — o usuário que realizou a ação. O campo user está presente nos status sent e canceled. Quando a oferta é recusada, rejectReason contém o motivo da recusa; quando é aceita ou recusada, answeredAt contém a data da resposta.

Condições de disparo por transição

TransiçãoObservação
notSentNão dispara job-offer.status-changed — utilize job-offer.created
sentPortal Gupy
viewedCandidato abriu a carta no portal
accepted / rejectedResposta do candidato
canceledCancelamento manual ou automático (nova oferta)

Campos condicionais por status (jobOffer.status)

StatussentAtansweredAtrejectReason
sentpreenchidonullnull
viewedpreenchidonullnull
acceptedpreenchidopreenchidonull
rejectedpreenchidopreenchidopreenchido
canceledpreenchido se já havia sido enviada; null se cancelada antes do envionullnull
❗️

Regra de cancelamento

O cancelamento manual só é permitido quando a oferta está em sent ou viewed. O cancelamento automático ocorre quando uma nova oferta é enviada (a oferta anterior já estava em sent ou viewed).

O agente integrador receberá os dados do webhook conforme contrato JSON do evento job-offer.status-changed.

❗️

Atenção!

O exemplo abaixo trata-se de um webhook modelo e não é indicado trabalhar o desenvolvimento a partir do mesmo. O ideal é que o desenvolvimento seja a partir de um payload real.

Exemplo de payload — transição para sent

{
    "eventId": "880e8400-e29b-41d4-a716-446655440003",
    "eventDate": "2026-06-20T10:00:00.000Z",
    "action": "job-offer.status-changed",
    "application": { "id": 12646330 },
    "job": { "id": 8685731 },
    "jobOffer": {
        "id": 12345,
        "status": "sent",
        "createdAt": "2026-06-18T09:00:00.000Z",
        "updatedAt": "2026-06-20T10:00:00.000Z",
        "answeredAt": null,
        "rejectReason": null,
        "sentAt": "2026-06-20T10:00:00.000Z",
        "salaryAmount": 8500.00,
        "salaryCurrency": "BRL",
        "admissionDate": "2026-07-01T00:00:00.000Z",
        "admissionType": "employee_admission"
    }
}

Exemplo de payload — transição para viewed

{
    "eventId": "770e8400-e29b-41d4-a716-446655440002",
    "eventDate": "2026-06-21T08:00:00.000Z",
    "action": "job-offer.status-changed",
    "application": { "id": 12646330 },
    "job": { "id": 8685731 },
    "jobOffer": {
        "id": 12345,
        "status": "viewed",
        "createdAt": "2026-06-18T09:00:00.000Z",
        "updatedAt": "2026-06-21T08:00:00.000Z",
        "answeredAt": null,
        "rejectReason": null,
        "sentAt": "2026-06-20T10:00:00.000Z",
        "salaryAmount": 8500.00,
        "salaryCurrency": "BRL",
        "admissionDate": "2026-07-01T00:00:00.000Z",
        "admissionType": "employee_admission"
    }
}

Exemplo de payload — transição para accepted

{
    "eventId": "550e8400-e29b-41d4-a716-446655440000",
    "eventDate": "2026-06-25T14:30:00.000Z",
    "action": "job-offer.status-changed",
    "application": { "id": 12646330 },
    "job": { "id": 8685731 },
    "jobOffer": {
        "id": 12345,
        "status": "accepted",
        "createdAt": "2026-06-18T09:00:00.000Z",
        "updatedAt": "2026-06-25T14:30:00.000Z",
        "answeredAt": "2026-06-25T14:30:00.000Z",
        "rejectReason": null,
        "sentAt": "2026-06-20T10:00:00.000Z",
        "salaryAmount": 8500.00,
        "salaryCurrency": "BRL",
        "admissionDate": "2026-07-01T00:00:00.000Z",
        "admissionType": "employee_admission"
    }
}

Exemplo de payload — transição para rejected

{
    "eventId": "660e8400-e29b-41d4-a716-446655440001",
    "eventDate": "2026-06-25T15:00:00.000Z",
    "action": "job-offer.status-changed",
    "application": { "id": 12646330 },
    "job": { "id": 8685731 },
    "jobOffer": {
        "id": 12345,
        "status": "rejected",
        "createdAt": "2026-06-18T09:00:00.000Z",
        "updatedAt": "2026-06-25T15:00:00.000Z",
        "answeredAt": "2026-06-25T15:00:00.000Z",
        "rejectReason": "O valor da oferta é menor do que mercado.",
        "sentAt": "2026-06-20T10:00:00.000Z",
        "salaryAmount": 8500.00,
        "salaryCurrency": "BRL",
        "admissionDate": "2026-07-01T00:00:00.000Z",
        "admissionType": "employee_admission"
    }
}

Exemplo de payload — transição para canceled

{
    "eventId": "990e8400-e29b-41d4-a716-446655440004",
    "eventDate": "2026-06-22T16:00:00.000Z",
    "action": "job-offer.status-changed",
    "application": { "id": 12646330 },
    "job": { "id": 8685731 },
    "jobOffer": {
        "id": 12345,
        "status": "canceled",
        "createdAt": "2026-06-18T09:00:00.000Z",
        "updatedAt": "2026-06-22T16:00:00.000Z",
        "answeredAt": null,
        "rejectReason": null,
        "sentAt": "2026-06-20T10:00:00.000Z",
        "salaryAmount": 8500.00,
        "salaryCurrency": "BRL",
        "admissionDate": "2026-07-01T00:00:00.000Z",
        "admissionType": "employee_admission"
    }
}

Possíveis erros

Recebimento no agente integrador
O ambiente precisa estar público para poder receber os dados do webhook na postbackUrl, caso contrário ocorrerá falha na entrega.

Configuração incorreta do clientHeaders
Caso o clientHeaders seja customizado sem a inclusão do Content-Type esperado pelo endpoint, a requisição pode ser enviada sem esse header ou com um valor divergente do necessário, causando falha no processamento pelo destino.

Cadeia de certificados SSL ausente, expirada ou incompleta
Se o certificado SSL do ambiente de destino estiver ausente, expirado ou com a cadeia incompleta (faltando certificados intermediários), a conexão pode ser recusada durante o handshake TLS, resultando em falha na entrega dos dados.


Did this page help you?