{
 "openapi": "3.1.0",
 "info": {
  "title": "CoverClicks API",
  "version": "2026-10-21",
  "summary": "Job-search reactivation signals for buyers (staffing firms, employers, lead buyers) and publishers (job sites).",
  "description": "CoverClicks tells a buyer which people on their own list have started job hunting again, and tells a publisher which of its dormant users are active again across the network. People are identified only by the SHA-256 of the trimmed, lowercased email address. Nothing returned ever names a site, a raw email, a date of activity or a hit count.\n\nHow a buyer uses it: (1) issue an API key on the dashboard API key tab (self-serve account at https://portal.coverclicks.com/buyer-apply, first 500 matches free, then $0.15 per delivered match); (2) upload the hashed list with POST /api/buyer/v1/dormant/upload, optionally with an external_id column holding your own record id; (3) receive matches as a daily CSV, a signed real-time webhook (see the `matchSignal` callback) or on demand through POST /api/buyer/v1/profile/lookup; or skip steps 2-3 by connecting your ATS/CRM at https://portal.coverclicks.com/buyer-dashboard/integrations (32 systems).\n\nHuman docs: https://www3.coverclicks.com/docs. Plain-text summary for assistants: https://www3.coverclicks.com/llms.txt.",
  "contact": {
   "name": "CoverClicks support",
   "email": "support@coverclicks.com",
   "url": "https://www3.coverclicks.com/docs/ai"
  },
  "termsOfService": "https://www3.coverclicks.com/terms"
 },
 "servers": [
  {
   "url": "https://portal.coverclicks.com",
   "description": "Buyer and publisher APIs"
  },
  {
   "url": "https://ingest.coverclicks.com",
   "description": "Publisher server-to-server events"
  }
 ],
 "security": [
  {
   "bearerKey": []
  }
 ],
 "tags": [
  {
   "name": "buyer",
   "description": "Needs a buyer API key (prefix ccb_), issued on the buyer dashboard."
  },
  {
   "name": "publisher",
   "description": "Needs a per-property publisher API key, issued at approval."
  }
 ],
 "paths": {
  "/api/buyer/v1/dormant/upload": {
   "post": {
    "tags": [
     "buyer"
    ],
    "operationId": "buyerUploadList",
    "summary": "Upload (replace) the buyer's dormant list",
    "description": "CSV body. Header row `hashed_email` (or `email`, hashed on arrival and never kept), optional second column `external_id` (your own record id, up to 128 chars, echoed on every delivered match). Each upload replaces the whole list. 20 uploads per hour, 800 MB max.",
    "requestBody": {
     "required": true,
     "content": {
      "text/csv": {
       "schema": {
        "type": "string"
       },
       "example": "hashed_email,external_id\n5d41402abc4b2a76b9719d911017c592a3b7e1f0c9d8e7f6a5b4c3d2e1f00a1b,cand_88213\n"
      }
     }
    },
    "responses": {
     "200": {
      "description": "Stored. Body carries the stored row count.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "rows": {
           "type": "integer"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "413": {
      "description": "Body over 800 MB.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/api/buyer/v1/profile/lookup": {
   "post": {
    "tags": [
     "buyer"
    ],
    "operationId": "buyerProfileLookup",
    "summary": "What the network knows about up to 100 people",
    "description": "Match = anyone the network has seen in the last 90 days. Each match found uses 1 of the account's matches (same pool as the free 500 / prepaid credits); a lookup that finds nothing is free. 3,000 requests per minute, 100 hashes per request. Data refreshes daily around 11:00 UTC.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "oneOf": [
         {
          "type": "object",
          "required": [
           "hashed_email"
          ],
          "properties": {
           "hashed_email": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
           }
          }
         },
         {
          "type": "object",
          "required": [
           "hashed_emails"
          ],
          "properties": {
           "hashed_emails": {
            "type": "array",
            "maxItems": 100,
            "items": {
             "type": "string",
             "pattern": "^[0-9a-f]{64}$",
             "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
            }
           }
          }
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "One result per distinct hash, in the order asked.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/BuyerLookupResponse"
        }
       }
      }
     },
     "400": {
      "description": "Not 64 hex chars, no hash sent, or more than 100.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "403": {
      "description": "Profile lookup switched off for this account."
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "description": "Temporarily unavailable, retry shortly."
     }
    }
   },
   "get": {
    "tags": [
     "buyer"
    ],
    "operationId": "buyerProfileLookupOne",
    "summary": "Look up one person",
    "parameters": [
     {
      "name": "hashed_email",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[0-9a-f]{64}$",
       "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Same shape as POST.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/BuyerLookupResponse"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     }
    }
   }
  },
  "/api/publisher/v1/profile/lookup": {
   "post": {
    "tags": [
     "publisher"
    ],
    "operationId": "publisherProfileLookup",
    "summary": "Occupations and employers the rest of the network has seen for a person",
    "description": "Your own contributions are removed before ranking. Available to publishers that contribute job_title or company data (email support to switch on). 100 hashes per request, 3,000 requests per minute; each match uses 1 monthly credit, a miss is free.",
    "parameters": [
     {
      "name": "X-Property-Id",
      "in": "header",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "hashed_email": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
         },
         "hashed_emails": {
          "type": "array",
          "maxItems": 100,
          "items": {
           "type": "string",
           "pattern": "^[0-9a-f]{64}$",
           "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Results in the order asked.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "results": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "hashed_email": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$",
              "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
             },
             "found": {
              "type": "boolean"
             },
             "status": {
              "type": "string",
              "enum": [
               "match",
               "no_match",
               "out_of_credits"
              ]
             },
             "occupations": {
              "type": "array",
              "maxItems": 3,
              "items": {
               "type": "object",
               "properties": {
                "title": {
                 "type": "string"
                },
                "soc": {
                 "type": [
                  "string",
                  "null"
                 ],
                 "description": "US SOC code when known"
                }
               }
              }
             },
             "employers": {
              "type": "array",
              "maxItems": 3,
              "items": {
               "type": "object",
               "properties": {
                "name": {
                 "type": "string"
                }
               }
              }
             }
            }
           }
          },
          "credits": {
           "type": "object",
           "properties": {
            "used": {
             "type": "integer"
            },
            "allotted": {
             "type": "integer"
            },
            "remaining": {
             "type": "integer"
            }
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/s2s/v1/events": {
   "post": {
    "tags": [
     "publisher"
    ],
    "servers": [
     {
      "url": "https://ingest.coverclicks.com"
     }
    ],
    "operationId": "publisherSendEvent",
    "summary": "Server-to-server event (one per request)",
    "description": "Alternative to the pixel for events only known server-side. Duplicates are accepted; send event_id so retries collapse. Add partner_extras.job_title and partner_extras.company on job pages to earn interest data.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PublisherEvent"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Accepted.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "status": {
           "type": "string",
           "const": "ok"
          },
          "message_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Malformed property id."
     },
     "401": {
      "description": "Key missing or not registered to that property."
     },
     "413": {
      "description": "partner_extras over 64 KB."
     },
     "422": {
      "description": "Missing required field, wrong type, or unknown field."
     }
    }
   }
  }
 },
 "webhooks": {
  "matchSignal": {
   "post": {
    "summary": "Real-time buyer match (we POST to the HTTPS address you set on the Reactivation signals tab)",
    "description": "One request per matched person. Answer any 2xx within 10 seconds. Verify `X-CoverClicks-Signature: sha256=<hex>` = HMAC-SHA256 of the raw body keyed with your signing secret (shown on the API key tab). On a non-2xx or timeout we resume from that person next run; event_id is stable so repeats can be ignored. Zapier, Make and n8n can receive this directly.",
    "parameters": [
     {
      "name": "X-CoverClicks-Signature",
      "in": "header",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "X-CoverClicks-Event",
      "in": "header",
      "schema": {
       "type": "string",
       "const": "buyer.match_signal"
      }
     },
     {
      "name": "X-CoverClicks-Source",
      "in": "header",
      "schema": {
       "type": "string",
       "const": "buyer_list"
      }
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MatchSignal"
       }
      }
     }
    },
    "responses": {
     "2XX": {
      "description": "Received."
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearerKey": {
    "type": "http",
    "scheme": "bearer",
    "description": "Buyer key (ccb_...) from the dashboard API key tab, or the per-property publisher key. Publisher lookup also needs X-Property-Id."
   }
  },
  "responses": {
   "Unauthorized": {
    "description": "Missing or wrong key.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "RateLimited": {
    "description": "Over the limit. resetSeconds says when to retry.",
    "content": {
     "application/json": {
      "schema": {
       "type": "object",
       "properties": {
        "error": {
         "type": "string"
        },
        "resetSeconds": {
         "type": "integer"
        }
       }
      }
     }
    }
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "string"
     },
     "message": {
      "type": "string"
     },
     "detail": {
      "type": "string"
     }
    }
   },
   "HashedEmail": {
    "type": "string",
    "pattern": "^[0-9a-f]{64}$",
    "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
   },
   "MatchSignal": {
    "type": "object",
    "description": "Same fields as the daily CSV columns (match_status,hashed_email,matched_at_utc,<six signals>,external_id).",
    "properties": {
     "event_id": {
      "type": "string"
     },
     "source": {
      "type": "string",
      "const": "buyer_list"
     },
     "buyer_id": {
      "type": "string",
      "format": "uuid"
     },
     "file_date": {
      "type": "string",
      "format": "date"
     },
     "hashed_email": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
     },
     "external_id": {
      "type": [
       "string",
       "null"
      ],
      "description": "Your own record id from the upload, or null."
     },
     "matched_at": {
      "type": "string",
      "format": "date-time"
     },
     "match_status": {
      "type": "string",
      "const": "hit"
     },
     "job_search_recency_days": {
      "type": [
       "integer",
       "null"
      ],
      "description": "Days since the person's last job-search activity. 7 or less is hot."
     },
     "job_search_intensity": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "low",
       "medium",
       "high",
       null
      ],
      "description": "From the number of active days in the last 30."
     },
     "job_search_duration_days": {
      "type": [
       "integer",
       "null"
      ],
      "description": "How long the current search has been going."
     },
     "cross_site_activity_count": {
      "type": [
       "integer",
       "null"
      ],
      "description": "How many network sites the person is active on. Sites are never named."
     },
     "dominant_occupation_cluster": {
      "type": [
       "string",
       "null"
      ],
      "description": "Main job area, e.g. Healthcare, when known."
     },
     "other_occupation_clusters": {
      "type": [
       "string",
       "null"
      ],
      "description": "Other job areas, semicolon separated. Blank when none."
     }
    }
   },
   "BuyerLookupResponse": {
    "type": "object",
    "properties": {
     "results": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "hashed_email": {
         "type": "string",
         "pattern": "^[0-9a-f]{64}$",
         "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
        },
        "found": {
         "type": "boolean"
        },
        "status": {
         "type": "string",
         "enum": [
          "match",
          "no_match",
          "out_of_credits"
         ]
        },
        "active_last_30_days": {
         "type": "boolean"
        },
        "job_search_recency_days": {
         "type": [
          "integer",
          "null"
         ],
         "description": "Days since the person's last job-search activity. 7 or less is hot."
        },
        "job_search_intensity": {
         "type": [
          "string",
          "null"
         ],
         "enum": [
          "low",
          "medium",
          "high",
          null
         ],
         "description": "From the number of active days in the last 30."
        },
        "job_search_duration_days": {
         "type": [
          "integer",
          "null"
         ],
         "description": "How long the current search has been going."
        },
        "cross_site_activity_count": {
         "type": [
          "integer",
          "null"
         ],
         "description": "How many network sites the person is active on. Sites are never named."
        },
        "dominant_occupation_cluster": {
         "type": [
          "string",
          "null"
         ],
         "description": "Main job area, e.g. Healthcare, when known."
        },
        "other_occupation_clusters": {
         "type": [
          "string",
          "null"
         ],
         "description": "Other job areas, semicolon separated. Blank when none."
        },
        "occupations": {
         "type": "array",
         "maxItems": 3,
         "items": {
          "type": "object",
          "properties": {
           "title": {
            "type": "string"
           },
           "soc": {
            "type": [
             "string",
             "null"
            ],
            "description": "US SOC code when known"
           }
          }
         }
        },
        "employers": {
         "type": "array",
         "maxItems": 3,
         "items": {
          "type": "object",
          "properties": {
           "name": {
            "type": "string"
           }
          }
         }
        }
       }
      }
     },
     "allowance_remaining": {
      "type": [
       "integer",
       "null"
      ],
      "description": "null on unlimited accounts."
     }
    }
   },
   "PublisherEvent": {
    "type": "object",
    "required": [
     "property_id",
     "hashed_email",
     "url"
    ],
    "additionalProperties": false,
    "properties": {
     "property_id": {
      "type": "string"
     },
     "hashed_email": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "SHA-256 hex (64 lowercase chars) of the trimmed, lowercased email address. No other transformation."
     },
     "url": {
      "type": "string",
      "format": "uri"
     },
     "event_type": {
      "type": "string",
      "enum": [
       "page_view",
       "form_submit",
       "click",
       "auth",
       "transaction"
      ],
      "default": "page_view"
     },
     "external_id": {
      "type": "string"
     },
     "event_id": {
      "type": "string"
     },
     "hashed_phone": {
      "type": "string",
      "description": "SHA-256 hex of the E.164 phone"
     },
     "user_ip": {
      "type": "string",
      "description": "Used only to derive a US zip at ingest, never stored"
     },
     "partner_extras": {
      "type": "object",
      "description": "Up to 64 KB; job_title and company feed interest data",
      "additionalProperties": true
     },
     "consent_metadata": {
      "type": "object",
      "properties": {
       "policy_version": {
        "type": "string"
       },
       "disclosure_tier": {
        "type": "string"
       }
      }
     },
     "page_title": {
      "type": "string"
     },
     "meta_description": {
      "type": "string"
     },
     "referrer": {
      "type": "string"
     },
     "privacy_policy_url": {
      "type": "string",
      "format": "uri"
     },
     "sdk_version": {
      "type": "string"
     }
    }
   }
  }
 }
}