{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://adderleydata.com/schemas/job.v1.json",
  "title": "Adderley Data job listing (job.v1)",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "id",
    "source",
    "market",
    "sourceJobId",
    "url",
    "title",
    "company",
    "advertiser",
    "location",
    "workArrangement",
    "employmentTypes",
    "salary",
    "classifications",
    "teaser",
    "bulletPoints",
    "postedAt",
    "updatedAt",
    "expiresAt",
    "isPromoted",
    "description",
    "changeType",
    "firstSeenAt",
    "contentHash",
    "scrapedAt"
  ],
  "properties": {
    "schemaVersion": {
      "const": "job.v1",
      "description": "Always \"job.v1\"."
    },
    "id": {
      "type": "string",
      "minLength": 3,
      "description": "Stable ID: source:market:sourceJobId."
    },
    "source": {
      "type": "string",
      "minLength": 1,
      "description": "Source key, e.g. \"seek\"."
    },
    "market": {
      "type": "string",
      "minLength": 2,
      "description": "Market key, e.g. \"au\"."
    },
    "sourceJobId": {
      "type": "string",
      "minLength": 1,
      "description": "The source's own job ID."
    },
    "url": {
      "type": "string",
      "pattern": "^https://",
      "description": "Canonical listing URL."
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "description": "Job title as listed."
    },
    "company": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "sourceCompanyId",
        "url"
      ],
      "description": "The hiring company, where the source names one.",
      "properties": {
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "sourceCompanyId": {
          "type": [
            "string",
            "null"
          ]
        },
        "url": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "advertiser": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "sourceAdvertiserId"
      ],
      "description": "The business that placed the listing. Never a person.",
      "properties": {
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "sourceAdvertiserId": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "location": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "raw",
        "suburb",
        "city",
        "region",
        "postcode",
        "country"
      ],
      "description": "Location as listed, plus parsed parts where the source provides them.",
      "properties": {
        "raw": {
          "type": [
            "string",
            "null"
          ]
        },
        "suburb": {
          "type": [
            "string",
            "null"
          ]
        },
        "city": {
          "type": [
            "string",
            "null"
          ]
        },
        "region": {
          "type": [
            "string",
            "null"
          ]
        },
        "postcode": {
          "type": [
            "string",
            "null"
          ]
        },
        "country": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[A-Z]{2}$"
        }
      }
    },
    "workArrangement": {
      "enum": [
        "on_site",
        "hybrid",
        "remote",
        "unknown"
      ],
      "description": "on_site, hybrid, remote or unknown."
    },
    "employmentTypes": {
      "type": "array",
      "items": {
        "enum": [
          "full_time",
          "part_time",
          "contract",
          "casual",
          "temporary",
          "internship",
          "volunteer",
          "unknown"
        ]
      },
      "description": "Normalised work types."
    },
    "salary": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "raw",
        "min",
        "max",
        "currency",
        "period",
        "includesSuper"
      ],
      "description": "Salary text as listed, parsed to numbers where it contains them.",
      "properties": {
        "raw": {
          "type": [
            "string",
            "null"
          ]
        },
        "min": {
          "type": [
            "number",
            "null"
          ],
          "minimum": 0
        },
        "max": {
          "type": [
            "number",
            "null"
          ],
          "minimum": 0
        },
        "currency": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[A-Z]{3}$"
        },
        "period": {
          "enum": [
            "hour",
            "day",
            "week",
            "month",
            "year",
            null
          ]
        },
        "includesSuper": {
          "type": [
            "boolean",
            "null"
          ]
        }
      }
    },
    "classifications": {
      "type": "array",
      "description": "The source's category and subcategory.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "category",
          "subcategory"
        ],
        "properties": {
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "subcategory": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      }
    },
    "teaser": {
      "type": [
        "string",
        "null"
      ],
      "description": "Short summary shown on the results page."
    },
    "bulletPoints": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Selling points shown on the results page."
    },
    "postedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When the listing was posted (UTC)."
    },
    "updatedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "The source's last-modified time (UTC), when it publishes one."
    },
    "expiresAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When the listing expires (UTC), if shown."
    },
    "isPromoted": {
      "type": "boolean",
      "description": "Paid placement on the source."
    },
    "description": {
      "description": "Full description. Null unless requested. Contact details redacted by default.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "text",
            "html",
            "contactsRedacted"
          ],
          "properties": {
            "text": {
              "type": "string"
            },
            "html": {
              "type": [
                "string",
                "null"
              ]
            },
            "contactsRedacted": {
              "type": "boolean"
            }
          }
        }
      ]
    },
    "changeType": {
      "enum": [
        "NEW",
        "UPDATED",
        "UNCHANGED",
        "REAPPEARED",
        "EXPIRED",
        null
      ],
      "description": "Incremental runs only: NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED."
    },
    "firstSeenAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "Incremental runs only: first time this listing was seen."
    },
    "contentHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "SHA-256 of the fields that define a change."
    },
    "scrapedAt": {
      "type": "string",
      "format": "date-time",
      "description": "When this row was produced (UTC)."
    }
  }
}
