{
  "openapi": "3.1.0",
  "info": {
    "title": "Robinsontjek API",
    "version": "2.0.0",
    "description": "Check people against the Danish Robinson list before you contact them. Every person gets a match level first (`match.level`: what we found, 0 to 5, with what matched); you decide which level you go by. For integrations, `status` (allowed, blocked or manual) is computed from your chosen level (`stop_level`, 1 by default). Every result names the list version it was checked against. Authenticate with `Authorization: Bearer rl_live_…` (test keys `rl_test_…` return fixed made-up results and are never billed)."
  },
  "servers": [
    {
      "url": "https://api.robinsontjek.dk"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "tags": [
    {
      "name": "public-api",
      "description": "Robinsontjek API v2"
    }
  ],
  "paths": {
    "/v2/checks": {
      "post": {
        "tags": [
          "public-api"
        ],
        "operationId": "createChecks",
        "summary": "Check up to 1.000 people synchronously",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "people"
                ],
                "properties": {
                  "people": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 1000,
                    "items": {
                      "$ref": "#/components/schemas/PersonInput"
                    }
                  },
                  "policy": {
                    "$ref": "#/components/schemas/MatchPolicy"
                  },
                  "stop_level": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Override the organization's stop line for this call"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Results in input order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "list_version",
                    "results"
                  ],
                  "properties": {
                    "list_version": {
                      "$ref": "#/components/schemas/ListVersionRef"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CheckResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v2/check-jobs": {
      "post": {
        "tags": [
          "public-api"
        ],
        "operationId": "createCheckJob",
        "summary": "Check a large batch asynchronously (JSON rows or a file)",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "people"
                ],
                "properties": {
                  "people": {
                    "type": "array",
                    "maxItems": 1000000,
                    "items": {
                      "$ref": "#/components/schemas/PersonInput"
                    }
                  },
                  "callback_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "When the job is done or has failed, we POST the CheckJob (the same JSON as GET /v2/check-jobs/{id}) to this\naddress. The request is signed like a webhook: header `Robinsontjek-Signature: t=<timestamp>,v1=<hmac>`,\nHMAC-SHA256 of `<timestamp>.<raw body>`, with the hex SHA-256 of the API key that created the job as the secret.\n`Robinsontjek-Event` is `check_job.completed` or `check_job.failed`. A reply of 2xx ends it; anything else\n(or no answer within 5 seconds) is tried again three times. https only.\n"
                  },
                  "external_ref": {
                    "type": "string"
                  },
                  "policy": {
                    "$ref": "#/components/schemas/MatchPolicy"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "column_mapping": {
                    "$ref": "#/components/schemas/ColumnMapping"
                  },
                  "callback_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "external_ref": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v2/check-jobs/{id}": {
      "get": {
        "tags": [
          "public-api"
        ],
        "operationId": "getCheckJob",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Job status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckJob"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v2/check-jobs/{id}/results": {
      "get": {
        "tags": [
          "public-api"
        ],
        "operationId": "getCheckJobResults",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 10000,
              "default": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CheckResult"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/company-checks": {
      "post": {
        "tags": [
          "public-api"
        ],
        "operationId": "createCompanyChecks",
        "summary": "Check companies against CVR reklamebeskyttelse (and sole-proprietor owners against Robinsonlisten)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "companies"
                ],
                "properties": {
                  "companies": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "$ref": "#/components/schemas/CompanyInput"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CompanyCheckResult"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/phone-checks": {
      "post": {
        "tags": [
          "public-api"
        ],
        "operationId": "createPhoneChecks",
        "summary": "Resolve phone numbers via the directory and check the person against Robinsonlisten",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phones"
                ],
                "properties": {
                  "phones": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "object",
                      "required": [
                        "phone"
                      ],
                      "properties": {
                        "phone": {
                          "type": "string",
                          "examples": [
                            "+4520202020",
                            "20 20 20 20"
                          ]
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "list_version": {
                      "$ref": "#/components/schemas/ListVersionRef"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CheckResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v2/list-versions/current": {
      "get": {
        "tags": [
          "public-api"
        ],
        "operationId": "getCurrentListVersion",
        "responses": {
          "200": {
            "description": "The list version checks currently run against",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListVersionRef"
                }
              }
            }
          }
        }
      }
    },
    "/v2/addresses/resolve": {
      "post": {
        "tags": [
          "public-api"
        ],
        "operationId": "resolveAddresses",
        "summary": "Parse and resolve free-text Danish addresses (not billed)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "addresses"
                ],
                "properties": {
                  "addresses": {
                    "type": "array",
                    "maxItems": 100,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resolved addresses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ResolvedAddress"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key: `Authorization: Bearer rl_live_…`"
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "CheckJob": {
        "type": "object",
        "required": [
          "id",
          "status",
          "source"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "type": "string",
            "enum": [
              "upload",
              "email",
              "sftp",
              "api",
              "monitor",
              "connector"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "awaiting_payment",
              "queued",
              "running",
              "done",
              "failed",
              "deleted"
            ]
          },
          "filename": {
            "type": [
              "string",
              "null"
            ]
          },
          "file_format": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "xlsx",
              "xls",
              "csv",
              null
            ]
          },
          "row_count": {
            "type": "integer"
          },
          "processed_count": {
            "type": "integer"
          },
          "allowed_count": {
            "type": "integer"
          },
          "blocked_count": {
            "type": "integer"
          },
          "manual_count": {
            "type": "integer"
          },
          "level_counts": {
            "$ref": "#/components/schemas/LevelCounts"
          },
          "columns": {
            "type": "array",
            "description": "Detected headers (draft only)",
            "items": {
              "type": "string"
            }
          },
          "column_mapping": {
            "$ref": "#/components/schemas/ColumnMapping"
          },
          "options": {
            "$ref": "#/components/schemas/CheckJobOptions"
          },
          "preview": {
            "type": "array",
            "description": "First 20 rows with their results (draft and done)",
            "items": {
              "type": "object",
              "properties": {
                "cells": {
                  "type": "array",
                  "items": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "result": {
                  "$ref": "#/components/schemas/CheckResult"
                }
              }
            }
          },
          "price": {
            "type": [
              "object",
              "null"
            ],
            "description": "What running this will cost (null on the annual plan)",
            "properties": {
              "product": {
                "type": "string",
                "enum": [
                  "list_small",
                  "list_large",
                  "custom"
                ]
              },
              "amount_cents": {
                "type": "integer"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "list_version": {
            "$ref": "#/components/schemas/ListVersionRef"
          },
          "certificate_token": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "delete_after": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CheckJobOptions": {
        "type": "object",
        "properties": {
          "check_people": {
            "type": "boolean",
            "default": true
          },
          "check_companies": {
            "type": "boolean",
            "default": false
          },
          "check_phones": {
            "type": "boolean",
            "default": false
          },
          "policy": {
            "$ref": "#/components/schemas/MatchPolicy"
          },
          "use_private_list": {
            "type": "boolean",
            "default": false
          },
          "stop_level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "The level this list is filtered on (defaults to the organization's)"
          }
        }
      },
      "CheckResult": {
        "type": "object",
        "required": [
          "status",
          "reasons"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "reasons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reason"
            }
          },
          "level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "unit",
              "floor",
              "building",
              "previous",
              "none",
              null
            ],
            "description": "Deprecated, use match.level. Where on the address the match was found."
          },
          "match": {
            "$ref": "#/components/schemas/Match"
          },
          "stop_level": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5,
            "description": "The organization's stop line. status = blocked when match.level >= stop_level."
          },
          "address": {
            "description": "How we read the address",
            "oneOf": [
              {
                "$ref": "#/components/schemas/ResolvedAddress"
              },
              {
                "type": "null"
              }
            ]
          },
          "phone_lookup": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "found",
                  "unlisted",
                  "not_found"
                ]
              },
              "name": {
                "type": "string"
              },
              "address": {
                "type": "string"
              }
            }
          }
        }
      },
      "ColumnMapping": {
        "type": "object",
        "description": "Maps our fields to the file's column index (0-based) or header name",
        "properties": {
          "header_row": {
            "type": "integer",
            "default": 0
          },
          "name": {
            "type": [
              "integer",
              "string"
            ]
          },
          "first_name": {
            "type": [
              "integer",
              "string"
            ]
          },
          "last_name": {
            "type": [
              "integer",
              "string"
            ]
          },
          "address": {
            "type": [
              "integer",
              "string"
            ]
          },
          "street": {
            "type": [
              "integer",
              "string"
            ]
          },
          "house_number": {
            "type": [
              "integer",
              "string"
            ]
          },
          "floor": {
            "type": [
              "integer",
              "string"
            ]
          },
          "door": {
            "type": [
              "integer",
              "string"
            ]
          },
          "postal_code": {
            "type": [
              "integer",
              "string"
            ]
          },
          "city": {
            "type": [
              "integer",
              "string"
            ]
          },
          "phone": {
            "type": [
              "integer",
              "string"
            ]
          },
          "cvr": {
            "type": [
              "integer",
              "string"
            ]
          },
          "company_name": {
            "type": [
              "integer",
              "string"
            ]
          },
          "external_id": {
            "type": [
              "integer",
              "string"
            ]
          }
        }
      },
      "CompanyCheckResult": {
        "type": "object",
        "required": [
          "status",
          "reasons"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "cvr": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "reasons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reason"
            }
          },
          "company_form": {
            "type": [
              "string",
              "null"
            ]
          },
          "synced_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CompanyInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "cvr": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "unauthorized",
                  "payment_required",
                  "invalid_request",
                  "rate_limited",
                  "not_found",
                  "list_not_ready"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable",
                "in the request's locale (Accept-Language da/en)": null
              },
              "details": {
                "type": "object"
              },
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "LevelCounts": {
        "type": "object",
        "description": "How many rows ended on each match level. `unread` counts rows with no level (an address we could not read, or no name). All keys are always present.",
        "required": [
          "0",
          "1",
          "2",
          "3",
          "4",
          "5",
          "unread"
        ],
        "properties": {
          "0": {
            "type": "integer"
          },
          "1": {
            "type": "integer"
          },
          "2": {
            "type": "integer"
          },
          "3": {
            "type": "integer"
          },
          "4": {
            "type": "integer"
          },
          "5": {
            "type": "integer"
          },
          "unread": {
            "type": "integer"
          }
        }
      },
      "ListVersionRef": {
        "type": "object",
        "required": [
          "id",
          "published_on"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "type": "string",
            "enum": [
              "cpr",
              "customer"
            ]
          },
          "published_on": {
            "type": "string",
            "format": "date",
            "description": "Date CPR published the list"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Match": {
        "type": "object",
        "description": "What we found on the Robinson list at this person's address.\n0 none, 1 household (someone else at the address), 2 surname, 3 given_name, 4 name_building (same name, other flat),\n5 name_address (same name and address). previous_address raises the level to at least 4.\nlevel is null when the address could not be read (status is then manual).\n",
        "required": [
          "level",
          "code"
        ],
        "properties": {
          "level": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 5
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "household",
              "surname",
              "given_name",
              "name_building",
              "name_address",
              null
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human text in the request locale, e.g. »Samme navn og adresse«"
          },
          "on": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "first_name",
                "last_name",
                "address",
                "unit"
              ]
            }
          },
          "previous_address": {
            "type": "boolean"
          }
        }
      },
      "MatchPolicy": {
        "type": "string",
        "enum": [
          "standard",
          "strict",
          "bdq"
        ],
        "description": "standard: a household-only match (address matches, no name agrees) → manual.\nstrict: household-only → blocked; partial name → blocked.\nbdq: reproduces BDQ: any match at the address → blocked.\n"
      },
      "PersonInput": {
        "type": "object",
        "description": "Give a full name or first/last name, and an address as one string or as parts. A kvhx is accepted for legacy clients.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Your own reference",
            "echoed back": null
          },
          "name": {
            "type": "string",
            "examples": [
              "Anne Marie Jensen"
            ]
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "address": {
            "type": "string",
            "examples": [
              "Stationsalleen 48, 1. b, 2770 Kastrup"
            ]
          },
          "street": {
            "type": "string"
          },
          "house_number": {
            "type": "string"
          },
          "floor": {
            "type": "string"
          },
          "door": {
            "type": "string"
          },
          "postal_code": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "kvhx": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          }
        }
      },
      "Reason": {
        "type": "string",
        "enum": [
          "name_and_address",
          "name_and_unit",
          "household",
          "partial_name",
          "previous_address",
          "address_unreadable",
          "address_not_found",
          "no_name",
          "company_protected",
          "phone_unlisted",
          "phone_not_found",
          "list_not_ready"
        ]
      },
      "ResolvedAddress": {
        "type": "object",
        "properties": {
          "input": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "street": {
            "type": [
              "string",
              "null"
            ]
          },
          "house_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "floor": {
            "type": [
              "string",
              "null"
            ]
          },
          "door": {
            "type": [
              "string",
              "null"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "formatted": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Stationsalleen 48, 1. b, 2770 Kastrup"
            ]
          },
          "confidence": {
            "type": "string",
            "enum": [
              "exact",
              "corrected",
              "unresolved"
            ]
          }
        }
      },
      "Status": {
        "type": "string",
        "enum": [
          "allowed",
          "blocked",
          "manual"
        ],
        "description": "allowed = Må kontaktes, blocked = Må ikke kontaktes, manual = Tjek manuelt"
      }
    }
  }
}