space.schedule.verify()
Check in your endpoint that a call came from the scheduler, for your audience, and from a job you expect.
Checks that a call to your endpoint came from the scheduler, was meant for your endpoint, and came from a job you expect. Use it in the endpoint a signed job calls. See Verify calls to your app.
function verify(
request: Request | { headers: Headers | Record<string, string | undefined> },
options: VerifyOptions
): Promise<SchedulerCall>Parameters
requestRequest or objectThe incoming call: a
Request, or any object with itsheaders, as aHeadersor a plain object. Only theAuthorizationheader is read, whatever its case.options.audiencestringThe audience your job’s calls carry: the job’s target URL without its query, or the
audienceyou set on the job. It cannot be empty. A job’ssigning.effectiveAudienceshows it.options.jobsstring[]The jobs allowed to call this endpoint, each as a job ID or as
projects/{project}/jobs/{job}. Any job is accepted when you leave it out.options.nownumberThe time to check the token against, in milliseconds since 1 January 1970. The current time when you leave it out.
What it checks
- The call carries
Authorization: Bearerand a JSON Web Token. - The token is signed with RS256, by a key the scheduler publishes, named by the token’s
kid. - Its issuer is the scheduler, and its audience includes
options.audience. - It has not expired and is already valid, allowing 30 seconds either way for clocks that differ.
- It names a job, and when you give
options.jobs, that job is one of them.
The scheduler’s public keys are fetched on the first call and kept for 5 minutes. A token signed with a key not among them fetches the keys again, at most once every 30 seconds, so a new key is picked up after the scheduler rotates its keys. See Key rotation.
verify() needs no API key and no project, so an endpoint that only receives calls can use space without one.
Returns
A promise of the call’s details:
{
"project": "01k5ztq3jb9y2a1m9v2xw8e0ha",
"job": "01m3c081d2wj4g63qcr53pa1yh",
"subject": "projects/01k5ztq3jb9y2a1m9v2xw8e0ha/jobs/01m3c081d2wj4g63qcr53pa1yh",
"expiresAt": "2026-09-29T08:40:00.000Z",
"claims": {
"iss": "https://scheduler-api.digitospace.com",
"sub": "projects/01k5ztq3jb9y2a1m9v2xw8e0ha/jobs/01m3c081d2wj4g63qcr53pa1yh",
"aud": "https://shop.example/tasks/reminders",
"exp": 1790671200,
"iat": 1790670600
}
}projectstringThe ID of the project the job belongs to.
jobstringThe ID of the job that made the call.
subjectstringThe token’s subject,
projects/{project}/jobs/{job}.expiresAtDateWhen the token expires, 10 minutes after it was made.
claimsobjectEvery claim the token carries:
iss,sub,aud(a string or a list),exp, andnbfandiatwhen it has them, in seconds since 1 January 1970.
Errors
| Code | When |
|---|---|
invalid_signature | The call has no bearer token, or the token cannot be read, is not signed with RS256 by a published key, is from another issuer or for another audience, has expired, is not valid yet, or names no job. error.status is 401 |
unexpected_job | The token is from a job that is not in options.jobs. error.status is 403 |
keys_unavailable | The scheduler’s public keys could not be fetched. It is retryable, so answer with a 5xx status, and the run is retried if its job allows it |
validation_failed | options.audience is missing or empty, or options has a field this page does not list |
Answer invalid_signature and unexpected_job with error.status, and do none of the work. See SpaceError.
Example
import { space, SpaceError } from "digitospace"
export async function POST(request: Request) {
try {
const call = await space.schedule.verify(request, {
audience: "https://shop.example/tasks/reminders",
jobs: ["01m3c081d2wj4g63qcr53pa1yh"],
})
console.log(`Sending reminders for job ${call.job}`)
return new Response(null, { status: 204 })
} catch (error) {
if (
error instanceof SpaceError &&
(error.code === "invalid_signature" || error.code === "unexpected_job")
) {
return new Response(error.message, { status: error.status })
}
return new Response("Could not check the call", { status: 503 })
}
}Last updated