For Developers

Create Audit Log Event

This model is part of an upcoming API update which is currently in beta and therefore subject to change.

POST https://ed.link/api/v2/audit/events

Record one or more Events. The request body may be a single Event object, or an array of up to 100 Event objects. Events are retained for 30 days, during which they can be viewed and searched. Upon receiving an Event, Edlink will enrich and analyze the event in the context of other recent Events from your platform in order to give you a better picture of what's happening in your system, and to automatically raise warnings around suspicious activity.

If you have defined a Schema for the Event's action, the Event is validated against it. A Schema with validation_level of strict will reject non-conforming Events; a lax Schema (the default) will accept them but flag them. If no Schema exists for the action, the Event is accepted as-is. When sending a batch, each Event is validated independently. If any Event fails validation, the entire request is rejected and no Events are recorded.

You must provide your application secret key in order to access this endpoint. This authentication mechanism is a little bit different than most other requests in our API because it is not made in the "scope" of one particular integration or person. You can find your application secret on the Edlink dashboard.

Request Body

The request body should contain either:

  1. A single Event object, or
  2. An array of Event objects (1–100).

actor, action, targets, and scope are required on each Event. institution is strongly encouraged. The action string is always lowercased before storage and Schema lookup (e.g. Assignment.Submit becomes assignment.submit). The context is strongly suggested. created_date is optional. When provided, it must be a non-empty ISO-8601 timestamp string (e.g. 2026-08-13T20:35:00.000Z) within the last 30 days and not more than 24 hours in the future. Numbers and other types are rejected. Values outside that window are rejected because Events outside the 30-day retention window are not kept. When omitted, Edlink stamps the UTC time the request was received — not when background processing later writes the Event. severity is optional and defaults to debug when omitted. Allowed values are debug, low, medium, high, and emergency. Mixed-case values are accepted and normalized to lowercase (e.g. HIGH becomes high). If an unrecognized value is supplied, the Event is still accepted, severity is set to debug, and a warning is returned. The data, before, and after properties are arbitrary and optional, but each must be less than 64 KiB when serialized as JSON. Requests where any of them meets or exceeds this limit will be rejected with a 413 Payload Too Large. data is stored as after. Do not send both data and after on the same Event. For update-tracking Events, send before and after (the object state before and after the change). For other Events, send data.

Single Event
{
    "action": "assignment.submit",
    "severity": "medium",
    "created_date": "2026-08-13T20:35:00.000Z",
    "actor": {
        "identifiers": [
            { "value": "00000000-0000-0000-0000-000000000000", "issuer": "edlink" }
        ],
        "type": "person"
    },
    "targets": [
        { "identifiers": [{ "value": "123", "issuer": "acme", "name": "Midterm Essay" }], "type": "assignment" },
        { "identifiers": [{ "value": "ABC", "issuer": "acme" }], "type": "class" }
    ],
    "scope": "acme-tenant-7f3a",
    "institution": "00000000-0000-0000-0000-000000000000",
    "context": {
        "user_agent": "Ladybird",
        "http_method": "POST",
        "http_status": "201",
        "path": "/our/custom/submit/function/path",
        "ip": "1.1.1.1",
        "query": "?title=Test"
    },
    "data": {
        "assignment_title": "Test",
        "student_work_url": "https://example.com/work",
    }
}
Update Event (before / after)
{
    "action": "resource.update",
    "actor": {
        "identifiers": [
            { "value": "00000000-0000-0000-0000-000000000000", "issuer": "edlink" }
        ],
        "type": "person"
    },
    "targets": [
        { "identifiers": [{ "value": "123", "issuer": "acme", "name": "Syllabus" }], "type": "resource" }
    ],
    "scope": "acme-tenant-7f3a",
    "before": {
        "resource_name": "Draft",
        "resource_type": "text"
    },
    "after": {
        "resource_name": "Published",
        "resource_type": "text"
    }
}
Batch of Events
[
    {
        "action": "assignment.submit",
        "actor": {
            "identifiers": [
                { "value": "00000000-0000-0000-0000-000000000000", "issuer": "edlink" }
            ],
            "type": "person"
        },
        "targets": [
            { "identifiers": [{ "value": "123", "issuer": "acme" }], "type": "assignment" }
        ],
        "scope": "acme-tenant-7f3a",
        "institution": "00000000-0000-0000-0000-000000000000"
    },
    {
        "action": "user.logout",
        "actor": {
            "identifiers": [
                { "value": "00000000-0000-0000-0000-000000000000", "issuer": "edlink" }
            ],
            "type": "person"
        },
        "targets": [
            { "identifiers": [{ "value": "00000000-0000-0000-0000-000000000000", "issuer": "edlink" }], "type": "person" }
        ],
        "scope": "acme-tenant-7f3a"
    }
]

Sample Request

axios.post('https://ed.link/api/v2/audit/events', {
    headers: {
        authorization: `Bearer ${application_secret_key}`
    },
    data: {
        actor: {
            identifiers: [
                { value: '00000000-0000-0000-0000-000000000000', issuer: 'edlink' },
                { value: 'acme_user_42', issuer: 'acme' }
            ],
            type: 'person'
        },
        action: 'assignment.submit',
        severity: 'medium',
        targets: [
            { identifiers: [{ value: '123', issuer: 'acme' }], type: 'assignment' },
            { identifiers: [{ value: 'ABC', issuer: 'acme' }], type: 'class' }
        ],
        scope: 'acme-tenant-7f3a',
        institution: '00000000-0000-0000-0000-000000000000',
        context: {
            user_agent: 'Chrome/123'
        }
    }
});

Sample Response

The response will have status 201 Created.

For a single Event, the response contains the newly recorded Event id. For a batch, the response contains an ids array in the same order as the submitted Events. If there are any validation errors that are still "acceptable" (like in the "context" field parsing, or in a lax Schema), they will appear in the $warnings array. Batch warnings are prefixed with the array index (e.g. [1]: ...).

{
    "$request": "00000000-0000-0000-0000-000000000000",
    "$data": {
        "id": "00000000-0000-0000-0000-000000000000",
    },
    "$warnings": [
        {
            "code": "INVALID_DATA",
            "message": "This is a human-readable description of the warning."
        }
    ]
}

Batch response:

{
    "$request": "00000000-0000-0000-0000-000000000000",
    "$data": {
        "ids": [
            "00000000-0000-0000-0000-000000000001",
            "00000000-0000-0000-0000-000000000002"
        ]
    }
}

If the Schema is strict, the endpoint will return status 400 Bad Request with the payload containing the $errors details. For batches, error messages reference the failing Event by index, e.g. [1].targets.

{
    "$request": "00000000-0000-0000-0000-000000000000",
    "$errors": [
        {
            "code": "INVALID_DATA",
            "message": "This is a human-readable description of the warning."
        }
    ]
}