Webhooks

AwardSpring calls you when something happens.

The REST API is how you ask AwardSpring for data. Webhooks are how AwardSpring tells you something changed, without you polling for it.

When an event occurs — an application is submitted, an award is disbursed — AwardSpring POSTs a signed JSON body to a URL you control.

Webhooks are turned on per institution and configured in the AwardSpring admin site, not through this API. There is no /webhooks endpoint. If you do not see the screen described below, ask your AwardSpring contact to enable webhooks for your institution.

Setting up an endpoint

In AwardSpring, go to Settings → API → Webhooks and add an endpoint. You will need:

FieldRules
URLMust be https://. Plain http:// is rejected.
EventsOne or more of the event types. You only receive what you subscribe to.
Notification emailOptional. Where AwardSpring writes when an endpoint starts failing permanently.

An institution can have up to 10 endpoints. Each endpoint gets its own signing secret, shown once on creation and beginning with whsec_.

An endpoint can be made inactive at any time. An inactive endpoint receives nothing, and so does every endpoint at an institution whose Webhooks setting has been switched off — the setting gates delivery itself, not just the admin screens.

The envelope

Every delivery has the same outer shape. Only data differs by event type.

{
"id": "evt_9f0d2b1c3d4e5f60a1b2c3d4e5f60718",
"type": "application.submitted",
"occurred_at": "2026-04-03T14:22:31.4820000Z",
"tenant_id": 42,
"api_version": "v1",
"data": {
"application_id": 12345,
"award_cycle_id": 678,
"status": "Submitted",
"is_auto_submit": false,
"submitted_at": "2026-04-03T14:22:30.0000000Z",
"applicant": {
"user_id": 9001,
"first_name": "Ada",
"last_name": "Nunez",
"email": "ada.nunez@example.edu",
"student_id": "STU-44120",
"external_id": "EXT-44120"
}
}
}
FieldMeaning
idUnique event identifier, evt_ prefixed. Deduplicate on this.
typeThe event type, e.g. award.created.
occurred_atISO-8601 UTC timestamp of when the event happened, not when it was delivered.
tenant_idYour institution’s internal identifier. Constant for a given endpoint.
api_versionPayload contract version. Currently v1 for every event.
dataEvent-specific body. See Event types.

A test send carries an extra "is_test": true alongside api_version. Real events never carry it.

Note that data uses snake_case, matching the REST API. Identifiers inside it are raw integers, again matching the REST API — "application_id": 12345, not "app_12345".

Headers on every delivery

POST /your-endpoint HTTP/1.1
Content-Type: application/json
X-AwardSpring-Signature: t=1775227351,v1=5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
X-AwardSpring-Event: application.submitted
X-AwardSpring-Event-Id: evt_9f0d2b1c3d4e5f60a1b2c3d4e5f60718
X-AwardSpring-Delivery-Attempt: 1
HeaderUse it for
X-AwardSpring-SignatureVerify this before trusting the body. See Verifying signatures.
X-AwardSpring-EventRouting, without parsing the body. Matches type.
X-AwardSpring-Event-IdDeduplication, without parsing the body. Matches id.
X-AwardSpring-Delivery-Attempt1 on the first try, incrementing on each retry.

What your endpoint must do

  1. Verify the signature. An unverified endpoint accepts anything anyone posts at it.
  2. Respond 2xx quickly. AwardSpring gives up on a delivery after 10 seconds and treats it as failed. Queue the work; do not do it inline.
  3. Tolerate duplicates. A retry after a timeout, a manual replay, or a network failure can deliver the same id more than once. Treat id as an idempotency key on your side.
  4. Tolerate new fields. Fields get added to data without a version change. Ignore what you do not recognise rather than rejecting the payload.

Ordering is not guaranteed. Each endpoint’s deliveries are independent, and a retried event can arrive after a later one. If order matters, sort on occurred_at rather than on arrival.

Where to go next