Outgoing Leads API

With the Outgoing Leads API you can create a custom destination – or Webhook – which means register a URL to receive requests every time a lead is created, updated or an activity happens.

This destination can be private to a single website or it can be made available to all Real Geeks customers.

How to create a destination for Real Geeks Leads

If you area a Real Geeks client just follow the steps to configure a Custom API destination.

If you are not a client first request API access. We will give you a Real Geeks test website and Real Geeks test account. If you are creating a destination private to a specific website and have no intention to make it available to all clients, you can skip this step and use your own website. Then visit our Lead Router configuration, login with the Real Geeks credentials given and follow the steps to configure a Custom API as destination.

At first your Custom API destination will be available only for this specific site, so you can visit the website, sign-up, perform searches, etc, to trigger requests to your API.

When you’re ready to go Live let us know and provide the following information:

  • Description: a description of your destination that will be visible to users connecting to it
  • Partner ID Label: a label for your Partner ID field, what it means to your system, ex.: “Account ID”, “Site Code”
  • Partner ID Help: is a longer description with instructions on how users will be able to access the Partner ID from your system, ex.: “In order to access your Account ID visit Settings from your Dashboard”

Request Types

We have two main entities: Lead and Activity.

A destination is a single url that accepts HTTP POST requests with a JSON body. To identify which type of request you are receiving X-Lead-Router-Action header

  • created: a new lead was created
  • updated: some contact details of an existing lead were updated
  • activity_added: one or more activities were added to an existing lead
  • user_updated: details of Lead Manager CRM user – Agent or Lender – changed

We also provide other request types to notify when a client has enabled or disabled your integration:

  • integration_enabled: client has enabled this destination
  • integration_disabled: client has disabled this destination

Lead Created

X-Lead-Router-Action: created

A lead was created. Usually it’s a new sign up on the website, but could also be a manually created lead in the Lead Manager CRM, or even a lead generated using other sources that our client integrated with.

activities field is usually present indicating the first actions performed by the user on the website, like which search was performed and what property they viewed.

For details on fields available see Leads.

Potential Seller Leads

A potential seller lead is a variation of a seller lead where contact information has not been provided.

Real Geeks websites have a property valuation tool, users enter an address and it provides an estimate sale value for that property. It also shows the last sold date and price.

In order to visualize this information users have to sign up, giving their contact details. If the user doesn’t finish the sign up process their contact information is unknown, all we have is the address they looked for. This is called a Potential Seller Lead.

Lead Updated

X-Lead-Router-Action: updated

Lead contact details were updated. For this request the following fields are set:

  • id: Lead id, same as given on creation
  • site_uuid
  • site_domain
  • partner_id
  • activities: could be present but it’s optional, in this case the lead details should be updated and these activities should be added. Activities are never removed or updated, only added.

For details on fields available see Leads.

Activity Added

X-Lead-Router-Action: activity_added

One or more activities are being added to a Lead. For this request the following fields are set:

  • id: Lead id, same as given on creation
  • site_uuid
  • site_domain
  • partner_id
  • activities: list of one or more activities. See Activities for details on available fields.

Activity Added requests are always sent after the corresponding New Lead requests. But sometimes they can arrive in a different order since the network isn’t always reliable.

User Updated

X-Lead-Router-Action: user_updated

Details of a user – Agents or Lenders – in the Lead Manager CRM, like name or email, have changed.

For details on fields available see Users.

Integration Enabled or Disabled

X-Lead-Router-Action: integration_enabled

Notify the destination that a client has enabled or disabled this integration for their site. Request body will contain:

  • partner_id: ID provided by the Real Geeks client when connecting to this destination
  • site_uuid: unique identifier of the site inside Real Geeks

Request Order

The normal order of requests is usually something like this:

  1. New lead created
  2. Add activities to lead
  3. Update leads
  4. Add more activities
  5. etc

Even though we always send New Lead requests first we cannot guarantee they will arrive before others. So your destination should be ready to handle requests like Add Activities before you have the corresponding lead.

Request Headers

All requests will include the following custom HTTP headers


Identify the request type being sent, see Request Types section above.


Contains an id for this message. This is helpful to identify retries of the same request. Here is an example value:

X-Lead-Router-Message-Id: 7ca66bef-c704-47f2-b46e-0638291bae85


Contains the signature of this request body using the configured secret key. See details on our Security section below.


User-Agent is a string with the format: “RealGeeks-LeadRouter/v1.0”, where v1.0 is the API version.

Minor changes to the API that shouldn’t affect clients will increase the minor version number (v1.1, v1.2, etc).

Major changes to the API that could potentially affect clients will be notified beforehand and when they take place the major version number will increase (v2.0, v3.0, etc).

Here is an example value:

User-Agent: RealGeeks-LeadRouter/1.0


We retry every time we get a response with status code different than 200 up to 8 times, here is the behavior:

  • Wait 10min before for the second, third and forth attempts
  • Wait 30min before the 5th attempt
  • 1h before the 6th
  • 2h before the 7th
  • 4h before the 8th

All attempts of the same request will contain the same value of X-Lead-Router-Message-Id header.

To avoid retries return status code 406


In order to receive requests from Real Geeks your API needs to be open to the internet. For security reasons you probably want to limit the requests you receive to those coming from Real Geeks. The easier way to do this is to setup a secret token and validate the request.

Validating requests from Real Geeks

When your secret token is set Real Geeks will create a hash with the request body. You can see our full implementation in Go below:

func sign(secret, requestBody []byte) string {
    mac := hmac.New(sha256.New, secret)
    return fmt.Sprintf("%x", mac.Sum(nil))

This hash will be available as header X-Lead-Router-Signature. We have a full example of APIs in Go and Python validating the signature below. Obviously, your language and server implementations may differ. There’s a couple important things to point out:

  • Only the full request POST body is used to build the hash, no headers
  • Using a plain string comparison == is not advised. Use a constant time string comparison function

Sample code in Python using Flask

# API server validating Real Geeks Signature
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
app.debug = True
SECRET = '12345'
@app.route("/", methods=["POST"])
def hello():
    signature = str(request.headers['X-Lead-Router-Signature'])
    mac = hmac.new(SECRET, request.data, hashlib.sha256)
    if not hmac.compare_digest(mac.hexdigest(), signature):
        return "invalid signature", 403
    return "ok"
if __name__ == "__main__":

Sample code in Go

// API server validating Real Geeks Signature
package main
import (
var secret = []byte("12345")    // your secret here
func main() {
	http.HandleFunc("/", handleWebhook)
	log.Print("Listening at 9090")
	http.ListenAndServe(":9090", nil)
func handleWebhook(w http.ResponseWriter, r *http.Request) {
	signature, err := hex.DecodeString(r.Header.Get("X-Lead-Router-Signature"))
	if err != nil {
		log.Printf("failed to decode signature header (%v)", err)
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("failed to read body (%v)", err)
	mac := hmac.New(sha256.New, secret)
	mymac := mac.Sum(nil)
	if !hmac.Equal(mymac, signature) {
		log.Printf("Signature doesn't match")