Introduction

Informations sur l’API

Notre API est construite à partir du framework django-rest-framework. Elle est actuellement disponible en http et https.

Les différents endpoints sont catégorisés dans des index de endpoints. Par exemple, en envoyant une requête GET sur l’index /restapi/quotation/, vous obtiendrez la liste de tous les endpoints qu’elle contient.

$ curl -XGET https://www.aladom.fr/restapi/offers/
{"job-offer/my":"http://www.aladom.fr/restapi/offers/job-offer/my/","subcategory":"http://www.aladom.fr/restapi/offers/subcategory/"}

Note

Pour améliorer la lisibilité des exemples de cette documentation, la sortie des commandes curl sera filtrée par la commande python -m json.tool de manière à indenter proprement le JSON.

$ curl -s -XGET https://www.aladom.fr/restapi/offers/ | python -m json.tool
{
    "job-offer/my": "http://www.aladom.fr/restapi/offers/job-offer/my/",
    "subcategory": "http://www.aladom.fr/restapi/offers/subcategory/"
}

Description des endpoints

Il est possible d’obtenir une description formelle de chaque endpoint en utilisant la méthode OPTIONS.

$ curl -XOPTIONS https://www.aladom.fr/restapi/offers/subcategory/
{
    "name": "Subcategory List",
    "description": "",
    "renders": [
        "application/json",
        "text/html"
    ],
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ]
}

Cette description contient le nom du endpoint et une description si nécessaire. La clé renders donne les types mime dans lesquelles la réponse peut être formattée (à renseigner dans le header Accept de votre requête si le type par défaut ne vous convient pas). La plupart du temps les types de réponse sont application/json pour les appels d’API “réels” et text/html pour les appels faits via les navigateur. Il est en effet possible de consulter l’API via votre navigateur : https://www.aladom.fr/restapi/offers/subcategory/

Authentification

Certains endpoints nécessitent cependant une authentification. Si vous êtes connecté sur votre compte aladom, vous pourrez accéder à l’interface web de l’API. Mais pour un appel classique d’API (via curl par exemple), vous devrez utiliser le token d’authentification qui vous a été fourni par Aladom.

$ curl -XOPTIONS https://www.aladom.fr/restapi/offers/job-offer/my/
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Allow: GET, POST, HEAD, OPTIONS
WWW-Authenticate: Token
{
    "detail": "Informations d'authentification non fournies."
}
$ token='fedcba9876543210deadbeef0123456789abcdef'
$ curl -H"Authorization: Token $token" -XOPTIONS https://www.aladom.fr/restapi/offers/job-offer/my/
HTTP/1.1 200 OK
Content-Type: application/json
Allow: GET, POST, HEAD, OPTIONS
{
    "name": "My Job Offer List",
    "description": "",
    "renders": [
        "application/json",
        "text/html"
    ],
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "actions": {
        "POST": {
            "id": {
                "type": "integer",
                "required": false,
                "read_only": true,
                "label": "Id"
            },
            "city": {
                "type": "field",
                "required": true,
                "read_only": false,
                "label": "Ville",
                "help_text": "Vous pouvez entrer l'ID de la ville sous forme d'entier, le code INSEE sous forme textuelle, ou bien l'alias (slug) sous forme textuelle."
            },
            "provider": {
                "type": "integer",
                "required": true,
                "read_only": false,
                "label": "Annonceur"
            },
            "title": {
                "type": "string",
                "required": true,
                "read_only": false,
                "label": "Titre",
                "max_length": 255
            },
            "published_on": {
                "type": "datetime",
                "required": false,
                "read_only": true,
                "label": "Publi\u00e9e le"
            },
            "state": {
                "type": "choice",
                "required": false,
                "read_only": true,
                "label": "\u00c9tat"
            },
            "hourly_wage": {
                "type": "float",
                "required": false,
                "read_only": false,
                "label": "Salaire horaire (brut)"
            },
            "wage": {
                "type": "json",
                "required": false,
                "read_only": false,
                "label": "info salaire",
                "wage_type": {
                    "type": "multiple choice",
                    "required": false,
                    "read_only": false,
                    "label": "Type de salaire",
                    "choices": [
                        {
                            "value": 1,
                            "display_name": "Horaire"
                        },
                        {
                            "value": 2,
                            "display_name": "Mensuel"
                        },
                        {
                            "value": 3,
                            "display_name": "Annuel"
                        },
                    ]
                },
                "wage": {
                    "type": "float",
                    "required": false,
                    "read_only": false,
                    "label": "Salaire",
                },
            },
            "application_email": {
                "type": "email",
                "required": false,
                "read_only": false,
                "label": "E-mail pour candidater",
                "max_length": 255
            },
            "contract_types": {
                "type": "multiple choice",
                "required": false,
                "read_only": false,
                "label": "Types de contrat",
                "choices": [
                    {
                        "value": 1,
                        "display_name": "CDI"
                    },
                    {
                        "value": 2,
                        "display_name": "CDD"
                    },
                    {
                        "value": 3,
                        "display_name": "Int\u00e9rim"
                    },
                    {
                        "value": 4,
                        "display_name": "Stage"
                    },
                    {
                        "value": 5,
                        "display_name": "Apprentissage"
                    },
                    {
                        "value": 6,
                        "display_name": "Autre"
                    }
                ]
            },
            "url": {
                "type": "field",
                "required": false,
                "read_only": true,
                "label": "Url"
            }
        }
    }
}

La réponse ci-dessus (qui a été tronquée pour ne pas prendre trop de place) présente la clé actions qui liste les champs qu’il est possible de récupérer ou envoyer.

Chaque champ est décrit par :

  • type le type de donnée attendue

  • required le caractère obligatoire ou non

  • read_only la possibilité ou non de modifier ce champ

  • label le nom du champ en français

  • help_text une éventuelle précision concernant ce champ

Certains types de champs précisent également d’autres informations comme les choix possibles par exemple.

Récupérer des données

Il existe deux grands types de endpoints permettant de récupérer des données. Les endpoints de type “liste” et ceux de type “item”.

Récupérer une liste

Les endpoints proposant une liste contenant peu d’éléments renvoient simplement un tableau JSON. Par exemple :

$ curl -XGET https://www.aladom.fr/restapi/offers/subcategory/
[
    {
        "id": 1,
        "name": "Aide aux personnes handicap\u00e9es",
        "slug": "aide-personnes-handicapees",
        "category": "Aide au handicap",
        "category_id": 1
    },
    {
        "id": 2,
        "name": "Langue des signes",
        "slug": "langue-des-signes",
        "category": "Aide au handicap",
        "category_id": 1
    },
    {
        "id": 3,
        "name": "Transport handicap\u00e9",
        "slug": "transport-handicape",
        "category": "Aide au handicap",
        "category_id": 1
    }
]

Dans certains cas cependant, le nombre de résultats est potentiellement trop grand pour être renvoyé en une seule réponse et doit être paginé. Dans ce cas, cet un dictionnaire qui est renvoyé avec les clés suivantes :

  • count le nombre total de résultats

  • next url vers la page suivante le cas échéant

  • previous url vers la page précédente le cas échéant

  • results la liste des résultats

$ curl -XGET https://www.aladom.fr/restapi/offers/job-offer/my/
{
    "count": 33233,
    "next": "http://www.aladom.fr/restapi/offers/job-offer/my/?limit=100&offset=100",
    "previous": null,
    "results": [
        {
            "id": 123456,
            "subcategory": {
                "id": 26,
                "slug": "cours-economie",
                "name": "Cours d'\u00e9conomie"
            },
            "city": {
                "id": 30640,
                "code": "74152",
                "slug": "lovagny",
                "name": "Lovagny"
            },
        },
        {
            "id": 654321,
        }
    ]
}

Récupérer un item

Pour récupérer un item en particulier en connaissant son ID, il suffit de rajouter l’ID à la fin de l’URL :

$ curl -XGET https://www.aladom.fr/restapi/offers/subcategory/26/
{
    "id": 26,
    "name": "Cours d'\u00e9conomie",
    "slug": "cours-economie",
    "category": "Soutien scolaire",
    "category_id": 5
}

Envoyer de données

Il y a également deux grands types de endpoints permettant d’envoyer des données. Les endpoints de création et ceux de modification.

Création d’un item

La création d’un item se fait en utilisant la méthode POST sur l’URL de listing. Les données doivent être envoyées au format JSON et respecter la spécification donnée par la méthode OPTIONS.

En cas de succès, vous obtiendrez une réponse 201 au format JSON, contenant les détails de l’objet créé.

$ curl -XPOST -H'Content-Type:application/json' -d '{"city": "rennes", "provider": 153514, "title": "test", "subcategory": 33, "content": "Test Test"}' https://www.aladom.fr/restapi/offers/job-offer/my/
{
    "adhoc_mission_hours": null,
    "adhoc_mission_price": null,
    "application_email": "",
    "application_phone": "",
    "city": {
        "code": "35238",
        "id": 13811,
        "name": "Rennes",
        "slug": "rennes"
    },
    "content": "Test Test",
    "contract_duration": null,
    "contract_types": [],
    "cv_required": false,
    "hourly_wage": null,
    "id": 1947561,
    "is_adhoc_mission": false,
    "is_full_time": false,
    "is_part_time": false,
    "latitude": null,
    "longitude": null,
    "minimum_age": 18,
    "minimum_experience": null,
    "origin_reference": "",
    "origin_source": "",
    "pole_emploi_id": null,
    "pole_emploi_reject_reason": "",
    "pole_emploi_state": "",
    "provider": 153514,
    "published_on": "2018-02-15T11:14:58.525928",
    "start_date": null,
    "state": 1,
    "subcategory": {
        "id": 33,
        "name": "M\u00e9nage",
        "slug": "menage"
    },
    "title": "test",
    "url": "http://www.aladom.fr/emploi/test-rennes-15qqx.html",
    "vehicle_required": false,
    "weekly_hours": null
}

En cas d’erreur vous obtiendrez une réponse 400 au format JSON. Elle contiendra un dictionnaire dont chaque clé sera un des champs erroné et la valeur sera un tableau contenant la description en français des erreurs rencontrées sur le champ en question.

$ curl -XPOST -H'Content-Type:application/json' -d '{"city": "rennes", "provider": 15354, "title": "test", "subcategory": 33}' https://www.aladom.fr/restapi/offers/job-offer/my/
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
    "provider": [
        "Vous ne gérez pas le compte du membre '15354'"
    ],
    "content": [
        "Ce champ est obligatoire."
    ]
}

Modification d’un item

La modification d’un item se fait par la méthode PATCH sur l’URL d’item. Elle fonctionne de la même manière que la création d’un item, à l’exception que les champs que vous ne souhaitez pas modifier ne sont pas requis.

$ curl -XPATCH -H'Content-Type: application/json' -d '{"city": "35278", "title": "Test"}' https://www.aladom.fr/restapi/offers/job-offer/my/1947561/
{
    "adhoc_mission_hours": null,
    "adhoc_mission_price": null,
    "application_email": "",
    "application_phone": "",
    "city": {
        "code": "35278",
        "id": 14146,
        "name": "Saint-Gr\u00e9goire",
        "slug": "saint-gregoire"
    },
    "content": "Test Test",
    "contract_duration": null,
    "contract_types": [],
    "cv_required": false,
    "hourly_wage": null,
    "id": 1947561,
    "is_adhoc_mission": false,
    "is_full_time": false,
    "is_part_time": false,
    "latitude": null,
    "longitude": null,
    "minimum_age": 18,
    "minimum_experience": null,
    "origin_reference": "",
    "origin_source": "",
    "pole_emploi_id": null,
    "pole_emploi_reject_reason": "",
    "pole_emploi_state": "",
    "provider": 153514,
    "published_on": "2018-02-15T11:14:58.525928",
    "start_date": null,
    "state": 1,
    "subcategory": {
        "id": 33,
        "name": "M\u00e9nage",
        "slug": "menage"
    },
    "title": "Test",
    "url": "http://www.aladom.fr/emploi/test-rennes-15qqx.html",
    "vehicle_required": false,
    "weekly_hours": null
}

Types de champs

integer

Les champs de type integer représentent un entier, présenté en tant que tel au format JSON.

Ces champs peuvent définir une valeur minimum et une valeur maximum, respectivement avec les clés max_value et min_value.

float

Les champs de type float représentent un nombre réel, présenté en tant que tel au format JSON.

Ces champs peuvent définir une valeur minimum et une valeur maximum, respectivement avec les clés max_value et min_value.

boolean

Les champs de type boolean représentent une information binaire pouvant prendre la valeur true ou false.

string

Les champs de type string représentent une chaine de caractères, présentée en tant que tel au format JSON. Le nombre de caractères peut être limité, ce qui est spécifiée par la clé max_length.

email

Les champs de type email sont des champs de type string destinés à recevoir une adresse e-mail.

datetime

Les champs de type datetime sont des champs de type string destinés à recevoir un horodatage au format ISO-8601.

choice

Les champs de type choice sont des champs dont les valeurs possibles sont limitées. La liste des choix possibles est précisée par la clé choices.

La clé choices se présente sous la forme d’une liste dont chaque élément est un choix possible, décrit par la clé value (la valeur effective) et la clé display_name (la signification en français).

multiple choice

Les champs de type multiple choice sont similaires aux champs de type choice mais se présentent sous la forme d’un tableau JSON, permettant de sélectionner plusieurs choix parmis les valeurs indiquées par la clé choices.

field

Les champs de type field sont des champs personnalisés. Si le nom du champ n’est pas suffisamment explicite, la valeur attendue est précisée dans la clé help_text.

nested object

Les champs de type nested object sont des champs qui contiennent eux-mêmes d’autres champs. La liste des champs imbriqués possibles est définie par la clé children.

Webhooks

Il vous est possible de mettre en place des webhooks de manière à être notifiés en direct lorsque certains événements se produisent de notre côté :

  • Transmission d’un lead

  • Transmission d’un contact direct

  • Transmission d’une nouvelle candidature

Mettre en place un webhook

Si vous avez déjà accès à notre API, il vous suffit d’envoyer une requête POST à l’adresse https://www.aladom.fr/restapi/utils/webhooks/setup/ en précisant l’URL sur laquelle vous voulez que les données vous soient envoyées. Vous recevrez en réponse le token d’authentification que nous utiliserons dans les requêtes que nous vous enverrons.

Si vous n’avez pas encore accès à notre API, contactez nous pour en demander un.

$ token='fedcba9876543210deadbeef0123456789abcdef'
$ curl -H"Authorization: Token $token" -XPOST -d 'url=https://exemple.fr/aladom.wh' https://www.aladom.fr/restapi/utils/webhooks/setup/
{
    "token": "A486kFbdNn1rvesf79ulN+ozyqPUqeRxc2aMBBwT",
    "url": "https://exemple.fr/aladom.wh"
}

Une fois cette opération effectuée, les événements vous serons envoyés par requête POST avec le header Authorization: Token A486kFbdNn1rvesf79ulN+ozyqPUqeRxc2aMBBwT

Un header X-User-ID contenant l’ID de l’utilisateur concerné sera aussi envoyé. Si vous gerez un réseau, ce header vous permettra de différencier les agences. Pour obtenir la liste des utilisateurs que vous gérez, vous pouvez envoyer une requête GET à l’adresse https://www.aladom.fr/restapi/user/member/my/managed_users/

Nous vous enverrons également un headers X-Webhook-Version contenant un numéro de version qui changera à chaque fois que des modifications seront apportées à nos webhooks. Notez cependant que chaque nouvelle version sera rétro-compatible, seuls des champs seront ajoutés.

Traiter les requêtes reçues

Les requêtes vous seront envoyées au format JSON. Le JSON envoyé comportera toujours la clé event. Comme tous les événements sont envoyés à la même adresse, cette clé permet de vous indiquer de quel type d’événement il s’agit. C’est la première chose à vérifier, pour brancher correctement la suite du traitement. Si la valeur de event ne fait pas partie des valeurs que vous supportez, renvoyez un status code 501 (Not Implemented).

Pour les autres clés, référez-vous à la documentation de chaque événement. Dans la plupart des cas, un objet vous sera envoyé dans la clé object et le type de cet objet sera précisé par la clé type.

Si nous recevons une réponse 500, 502, 503, 504 ou si une erreur de connexion survient, nous ferons jusqu’à 4 nouvelles tentatives avant d’abandonner définitivement. D’autre part, si votre serveur ne répond pas en moins de 10 secondes, la requête sera abandonnée.

Sauf cas particulier précisé dans la documentation de chaque événement, nous n’attendons aucune réponse particulière et toutes les réponses autres que 500, 502, 503 et 504 seront traitées de la même manières. Cependant, nous loggons le status code reçu pour chaque requête envoyée. Pour débugger d’éventuels problèmes, il est donc préférable de retourner un code HTTP pertinent.

Exemple d’implémentation en Python

import json
import warnings

from django.views.generic import View


class AladomWebhookView(View):

    def get(self, request, *args, **kwargs):
        authorization = request.META.get('HTTP_AUTHORIZATION')
        if authorization != 'Token {}'.format(settings.ALADOM_WEBHOOK_TOKEN)
            response = HttpResponse(status=401)
            response['WWW-Authenticate'] = 'Token realm="aladom webhook"'
            return response
        version = request.META.get('HTTP_X_WEBHOOK_VERSION')
        if version != settings.ALADOM_WEBHOOK_VERSION:
            warnings.warn(
                "New Aladom Webhook version available : {}".format(version)
            )
        event = json.loads(request.body.decode('utf-8'))
        if event['event'] == 'new_lead':
            handler = self.handle_new_lead
        elif event['event'] == 'new_candidacy':
            handler = self.handle_new_candidacy
        elif event['event'] == 'new_contact_quote':
            handler = self.handle_new_contact_quote
        else:
            warnings.warn("Unknown Aladom event: {}".format(event['event']))
            return HttpResponse(status=501)
        response = handler(event)
        if response:
            return response
        return HttpResponse(status=204)

    def handle_new_lead(self, event):
        if event['type'] == 'quotation.CleaningQuoteRequest':
            handler = self.record_cleaning_lead
        elif event['type'] == 'quotation.HomeFurnishingsQuoteRequest':
            handler = self.record_home_furnishings_lead
        elif event['type'] == 'quotation.CraftsQuoteRequest':
            handler = self.record_crafts_lead
        elif event['type'] == 'quotation.HelpQuoteRequest':
            handler = self.record_help_lead
        elif event['type'] == 'quotation.ChildcareQuoteRequest':
            handler = self.record_childcare_lead
        elif event['type'] == 'quotation.MusicQuoteRequest':
            handler = self.record_music_lead
        elif event['type'] == 'quotation.TutoringQuoteRequest':
            handler = self.record_tutoring_lead
        else:
            warnings.warn("Unknown Aladom lead type: {}".format(event['type']))
            return HttpResponse(status=501)
        return handler(event['object'])

    def handle_new_candidacy(self, event):
        pass

    def handle_new_contact_quote(self, event):
        pass

    def record_cleaning_lead(self, lead):
        pass

    def record_home_furnishings_lead(self, lead):
        pass

    def record_crafts_lead(self, lead):
        pass

    def record_help_lead(self, lead):
        pass

    def record_childcare_lead(self, lead):
        pass

    def record_music_lead(self, lead):
        pass

    def record_tutoring_lead(self, lead):
        pass

Modifier les informations du webhook

Si vous avez perdu le token d’authentification, s’il a été corrompu ou si l’URL du webhook a changé, vous pouvez rappeler le même endpoint pour nous communiquer la nouvelle URL et régénérer le token.

$ token='fedcba9876543210deadbeef0123456789abcdef'
$ curl -H"Authorization: Token $token" -XPOST -d 'url=https://www.exemple.com/aladom.wh' https://www.aladom.fr/restapi/utils/webhooks/setup/
{
    "token": "1CtBRWjgmBeQmPipqk8jj/5H0mFDP272MMz1TDme",
    "old_url": "https://exemple.fr/aladom.wh",
    "url": "https://www.exemple.com/aladom.wh"
}