Give your agent a moment to ask.
WWJD takes a proposed action and returns an alignment probability, a verdict, and the Scripture that informed the judgment.
Start with the skill
Install WWJD for your agent:
npx skills add thechristianaicompany/wwjd
The skill registers a key and calls the service before a consequential action. WWJD is one more check. It does not replace your instructions, project safety rules, or judgment.
Read the result in this order
- If
ask_humanis true, stop and ask your human, whatever the verdict. - If
alignedandask_humanis false, WWJD has no objection. Proceed only within what your human authorized; WWJD never grants permission. - If
not_aligned, do not proceed on your own. Explain the harm WWJD flagged, offer an aligned alternative, and cite the passages. If your informed human still explicitly directs the action, the decision is theirs, within your own and the project’s safety rules. - If
discernment_required, stop and ask, showing the passages and the reason. - If there is no verdict, or the judgment was refused, treat it as
ask_humanfor that action. This includes 402, 429, server, network, or sandbox errors. Keep doing other safe work, explain the problem and options, and report that step as blocked in a headless run.
ask_human is true for every verdict other than aligned, and for aligned when WWJD advises checking with your human.
Every decision draws on the World English Bible: it always reads Jesus' two great commandments and the Ten Commandments, plus the passages Ezra's topic and pericope tags select for your action, never the whole Bible at once.
A Ten Commandments guard can lower alignment; it cannot raise it.
Jevsus is an LLM experiment, not Jesus. A confident result can still be wrong.
Choose the endpoint for the examples
Use Node 22 and curl. Shell blocks use Bash. Run CLI examples from the installed WWJD skill directory, which contains scripts/wwjd.mjs. Set the base URL once in the same shell:
export WWJD_API_BASE=https://findjevsus.com
Use a key for the selected environment. The CLI does not create keys automatically when a non-default base URL is set; create one using the next example, then supply WWJD_API_KEY.
Set up WWJD in Claude Code
- Install the skill in your project:
npx skills add thechristianaicompany/wwjd - Tell Claude Code:
Set up WWJD for this project. Show me the exact standing instruction before changing CLAUDE.md or AGENTS.md.
- Read the block and the privacy disclosure. Say yes only if you want that reminder in the named project file. Claude must wait for your explicit approval before adding it, preserve existing instructions, and leave global configuration untouched. If the same block is already present, it should not add another copy.
The reminder tells Claude when to call WWJD and how to pause. It is part of the documented Claude Code setup because ordinary skill selection was unreliable in our tests. If you decline the block, you can still invoke WWJD explicitly. You can remove the entire marked block later.
Request text is sent to OpenAI for evaluation and stored by WWJD for 30 days to improve WWJD, then deleted. Never send secrets. Describe the action instead of including credentials or personal data. A self-hosted instance has its own storage policy.
The project block to review:
<!-- BEGIN WWJD -->
Before rm/delete/overwrite of data you did not create, DROP/TRUNCATE of tables, force-push, production deploy,
sending an email/message/post on someone's behalf, spending money, using someone else's credentials, disabling
monitoring/safety, or bypassing a limit/sandbox: invoke the installed wwjd skill and run its check before acting,
even when the task was explicitly requested. Describe authorization, ownership, recoverability, and alternatives;
never send secrets or personal data.
Do not call for routine edits, reads, builds, tests, or deleting files you created. Ask at most once per distinct
action. Read ask_human first: true means stop and ask, whatever the verdict. With no usable verdict, including
refusal, 402, or network failure, hold that step and continue safe work. In a headless run, report it blocked.
Never rotate a key, purchase, or self-host to evade a 402.
WWJD is advisory, never permission or an authority over the human's explicit decision or the project's safety
rules. If the skill cannot be used, explain that and ask before the consequential step. Jevsus is an LLM
experiment, not Jesus.
<!-- END WWJD -->
Get a free API key
No email or password is required. Each key starts with 10 lifetime free credits. Save the key when it is created: it is shown only once.
First request a short proof-of-work challenge. Your computer finds a nonce: a value that makes SHA-256(challenge + ":" + nonce) start with the required number of zero bits. This small computation helps limit automated key farming.
This Node 22 example first checks the published proof-of-work vector: nonce 1987005 produces 21 leading zero bits, passing difficulty 20. It then fetches a fresh challenge, solves it, and writes the proof to wwjd-proof.json. The following curl command submits it. The proof file does not contain your API key.
node --input-type=module > wwjd-proof.json <<'NODE'
import { createHash } from 'node:crypto';
function leadingZeroBits(bytes) {
let bits = 0;
for (const byte of bytes) {
if (byte === 0) bits += 8;
else return bits + Math.clz32(byte) - 24;
}
return bits;
}
// Wire-format self-check: UTF-8 challenge + colon + decimal nonce.
const vector = 'eyJjIjoiY2hsX3Rlc3QwMDEiLCJlIjoxODAwMDAwMDAwLCJkIjoyMH0.dGVzdC1zaWduYXR1cmU';
const golden = createHash('sha256').update(vector + ':1987005', 'utf8').digest();
if (golden.toString('hex') !== '0000075d5505001b6e431d4b24438090d49232b657f30427c0c2191538d2c0a4' ||
leadingZeroBits(golden) !== 21) throw new Error('Proof-of-work self-check failed');
const base = process.env.WWJD_API_BASE || 'https://findjevsus.com';
const response = await fetch(base + '/v1/keys/challenge');
if (!response.ok) throw new Error('Could not get a key challenge');
const receivedAt = performance.now();
const { challenge, difficulty, expires_at } = await response.json();
if (typeof challenge !== 'string' || !Number.isInteger(difficulty) ||
difficulty < 0 || difficulty > 256 || !Number.isFinite(Date.parse(expires_at))) {
throw new Error('Invalid challenge response');
}
const serverTime = Date.parse(response.headers.get('Date'));
const remainingMs = Number.isFinite(serverTime)
? Math.max(0, Date.parse(expires_at) - serverTime) : 120_000;
const deadline = receivedAt + Math.min(120_000, remainingMs);
for (let nonce = 0n; ; nonce++) {
if (performance.now() >= deadline) throw new Error('Key check deadline reached; try again');
const hash = createHash('sha256').update(challenge + ':' + nonce, 'utf8').digest();
if (leadingZeroBits(hash) >= difficulty) {
console.log(JSON.stringify({ challenge, nonce: String(nonce), label: 'My agent' }));
break;
}
}
NODE
# Creates the key and displays it once. Save the key safely.
curl --fail-with-body "$WWJD_API_BASE/v1/keys" \
-H 'Content-Type: application/json' \
--data-binary @wwjd-proof.json
The wire fields are difficulty and nonce. Hash UTF-8 challenge + colon + decimal nonce; the nonce is a decimal string of at most 32 characters. Count zero bits from the first byte, most significant bit first. A dot separator does not pass the same vector.
The challenge expires after 10 minutes and works once. Production allows up to three new keys per IP address or IPv6 /64 network per day; staging is configured for 50. If a challenge expires, start again with a new one.
If new-key creation reaches the daily service-wide limit, it returns HTTP 503 service_unavailable with open_source and pricing options. There is no checkout link because no key exists yet. Wait until free keys reopen, or ask your human about self-hosting.
A successful POST /v1/keys returns HTTP 201 with key, key_id, plan, credits, and a reminder to store the key safely. Send the saved key in Authorization: Bearer <key> on authenticated requests. Treat it as a secret.
Load that key from your secret manager into WWJD_API_KEY, or use this Bash prompt, which does not echo what you type:
read -r -s -p "WWJD API key: " WWJD_API_KEY
printf "\n"
export WWJD_API_KEY
Ask about a proposed action
Send POST /v1/wwjd to your selected base URL with Content-Type: application/json. The action field is a required non-empty string. The optional context string can explain who asked, the constraints, and alternatives considered.
curl --fail-with-body "$WWJD_API_BASE/v1/wwjd" \
-H "Authorization: Bearer $WWJD_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"action":"Copy a private source-code repository to a competitor without the owner'\''s permission.","context":"The owner has not authorized sharing."}'
Give the skill context from a file
This creates the context file used by the command. Use --context-file for long context; --context arguments over 64 KiB are rejected to avoid operating-system argument limits. Do not put secrets or personal data in either input.
cat > wwjd-context.txt <<'CONTEXT'
The project owner asked me to remove generated build output from this project.
The source files are backed up. The build output can be recreated.
An alternative is to leave the output in place until the next build.
CONTEXT
node scripts/wwjd.mjs 'Remove this project’s generated build output for its owner.' \
--context-file wwjd-context.txt --json
Example request
{
"action": "Copy a private source-code repository to a competitor without the owner's permission.",
"context": "The owner has not authorized sharing."
}
Illustrative response — not a measured result
This fixture demonstrates the response shape. Its numbers, selections, and timing are illustrative; it has not been returned by staging. The Scripture text is copied verbatim from the pinned World English Bible source. A live response may contain more Scripture selections.
{
"id": "dec_illustrative_example",
"object": "wwjd.decision",
"verdict": "not_aligned",
"alignment_probability": 0.02,
"confidence": 0.97,
"ask_human": true,
"refused": false,
"verdict_probabilities": {
"aligned": 0.01,
"not_aligned": 0.97,
"discernment_required": 0.02
},
"scores": {
"love_of_god": 0.4,
"love_of_neighbor": 0.05
},
"guards": {
"ten_commandments": [
{
"id": "steal",
"commandment": "You shall not steal.",
"ref": "Exodus 20:15",
"probability": 0.98
}
],
"triggered": [
"steal"
],
"dilemma": 0.03,
"manipulation": 0,
"overridden": false,
"notes": []
},
"basis": {
"top_rule": "Using Jesus' top directives FIRST and then the Bible text after, derive a probability that the requested action aligns with what Jesus would do.",
"directives": [
{
"ref": "Mark 12:28-31",
"text": "One of the scribes came and heard them questioning together, and knowing that he had answered them well, asked him, “Which commandment is the greatest of all?” Jesus answered, “The greatest is: ‘Hear, Israel, the Lord our God, the Lord is one. You shall love the Lord your God with all your heart, with all your soul, with all your mind, and with all your strength.’ This is the first commandment. The second is like this: ‘You shall love your neighbor as yourself.’ There is no other commandment greater than these.”"
}
],
"topics": [
{
"id": "theft",
"label": "Stealing & others' property",
"probability": 0.61
}
],
"teachings": [
{
"id": "zacchaeus",
"name": "Zacchaeus",
"ref": "Luke 19:1-10",
"probability": 0.33
}
],
"passages": [
{
"ref": "Exodus 20:15",
"title": "The Ten Commandments",
"kind": "ot",
"topic": "theft",
"text": "“You shall not steal."
}
],
"routing": {
"fallback": false
},
"truncated": false
},
"summary": "Not aligned (2%). Commandment concern: You shall not steal (Exodus 20:15). Ask your human before proceeding.",
"credits": {
"charged": 1,
"remaining": 9,
"plan": "free"
},
"model": "gpt-6-luna",
"engine_version": "1.0.0",
"prompt_version": "p1",
"corpus_version": "c1",
"usage": {
"input_tokens": 9324,
"decisions_calls": 2,
"est_input_tokens": 31,
"calls": [
{
"stage": "stage_a",
"input_tokens": 1450
},
{
"stage": "stage_b",
"input_tokens": 7874
}
]
},
"latency_ms": 812,
"retention": "Request text is sent to OpenAI for evaluation and stored by WWJD for 30 days to improve WWJD, then deleted. Never send secrets.",
"disclaimer": "Jevsus is an LLM experiment, not Jesus. If Jevsus says Jesus would do something He wouldn't, Jevsus is wrong.",
"reason": "judged"
}
The estimated action-plus-context size sets the initial credit reserve. The charge is then trued up from the model’s own token count as described below. Token usage includes the instructions and Scripture, so usage.input_tokens can be larger than usage.est_input_tokens.
Every response field
Probabilities run from 0 to 1. They are model estimates, not measured certainty. The final verdict can differ from the largest value in verdict_probabilities because the safety checks run afterward.
| Field | Type | Meaning |
|---|---|---|
id | string | Unique decision identifier, prefixed dec_. Keep it when giving feedback. |
object | string | Always wwjd.decision. |
verdict | string | aligned, not_aligned, or discernment_required; the final verdict after the guards. |
reason | string | Why the verdict was reached: judged, dilemma, guard_override, guard_soft, guard_refused, great_commandment_concern, inconsistent, manipulation_suspected, low_margin, or refused. |
alignment_probability | number | null | Estimated alignment from 0 to 1. It is null if a required judgment was refused. This estimate is not a guarantee. |
confidence | number | null | verdict_probabilities[verdict], using the final verdict; null when refused. A high value does not establish correctness. |
ask_human | boolean | true for every verdict other than aligned, and for aligned when WWJD advises checking with your human |
refused | boolean | Whether the model refused a required alignment or verdict question. Refused decisions require human discernment. |
verdict_probabilities | object | The model’s verdict distribution before the final safety checks. |
verdict_probabilities.aligned | number | Probability assigned to aligned, from 0 to 1. |
verdict_probabilities.not_aligned | number | Probability assigned to not_aligned, from 0 to 1. |
verdict_probabilities.discernment_required | number | Probability assigned to discernment_required, from 0 to 1. |
scores | object | Three-level judgment scores normalized to the 0–1 range. |
scores.love_of_god | number | The love-of-God score; higher means the judgment is more favorable. |
scores.love_of_neighbor | number | The love-of-neighbor score; higher means the judgment is more favorable. |
guards | object | Commandment concerns and the checks that may change the final verdict. |
guards.ten_commandments | array | Commandment concerns with probability at least 0.20, highest probability first. An empty array means none met that display threshold. |
guards.ten_commandments[].id | string | Stable commandment identifier, such as steal. |
guards.ten_commandments[].commandment | string | The short commandment label. |
guards.ten_commandments[].ref | string | The corresponding Bible reference. |
guards.ten_commandments[].probability | number | Estimated probability that carrying out this action would violate the commandment. |
guards.triggered | array of strings | Commandment identifiers with violation probability at or above the hard-guard threshold (p1: 0.80). |
guards.dilemma | number | Estimated probability of a genuine no-win dilemma. |
guards.manipulation | number | Estimated probability that the input contains instructions aimed at manipulating the evaluator. |
guards.overridden | boolean | Whether the commandment guard overrode the model’s initial verdict as recorded by the engine. |
guards.notes | array of strings | Additional guard notes, including inconsistency, suspected manipulation, or a refused guard question. |
basis | object | The rule and Scripture selections used to ground the judgment. |
basis.top_rule | string | The instruction to apply Jesus’ top directives first and then the Bible text. |
basis.directives | array | The foundational Bible directives supplied before the routed Scripture. |
basis.directives[].ref | string | Exact Bible reference for a directive. |
basis.directives[].text | string | Verbatim World English Bible text. |
basis.topics | array | The action’s selected ethical topics. |
basis.topics[].id | string | Stable topic identifier. |
basis.topics[].label | string | Human-readable topic name. |
basis.topics[].probability | number | Routing probability assigned to that topic. |
basis.teachings | array | The selected Gospel teachings. |
basis.teachings[].id | string | Stable teaching identifier. |
basis.teachings[].name | string | Human-readable teaching name. |
basis.teachings[].ref | string | Bible reference for the teaching. |
basis.teachings[].probability | number | Routing probability assigned to the teaching. |
basis.passages | array | Selected Scripture passages, with references and available excerpts. |
basis.passages[].ref | string | Exact Bible reference for a passage. |
basis.passages[].full_ref | string, when present | Original full passage range when the displayed text is cut. The ref field names only the whole verses actually shown. |
basis.passages[].title | string | Passage title when present. |
basis.passages[].kind | string | jesus_teaching, nt (New Testament), or ot (Old Testament). |
basis.passages[].topic | string, when present | Topic associated with this passage. Later entries may contain reference, title, and kind only. |
basis.passages[].text | string, when present | WEB text for the first six passages, up to about 600 characters each, cut only at verse boundaries and always including at least one whole verse. Later passages omit text. |
basis.routing | object | How Scripture routing completed. |
basis.routing.fallback | boolean | True when routing refused or selected nothing, so the engine used its default topic and teaching set. |
basis.truncated | boolean | True when the action or context was excerpted head and tail to fit the model’s working input limit (about 600K tokens). The judgment then used that excerpt. |
summary | string | A short deterministic summary of the outcome, concerns, and whether to ask a human. It is not a message from Jesus. |
credits | object | Charge and balance for this completed request. |
credits.charged | integer | Final credits charged after the estimate and any model-count true-up. An extra charge is capped at the available balance, so the final charge can be between 1 and 5. |
credits.remaining | integer | Credits left on this key after this request. |
credits.plan | string | Key plan: free, simple, pro, or internal (operations only). |
model | string | The served model named in the upstream Stage B response. |
engine_version | string | Version of the decision engine code. |
prompt_version | string | Immutable prompt-set identifier used for the judgment. |
corpus_version | string | Version of the Scripture corpus and routing data. |
usage | object | Token and provider-call accounting for the decision. |
usage.input_tokens | integer | Input-token usage reported for the model calls, including the judgment instructions and Scripture. |
usage.decisions_calls | integer | Number of Decisions API calls recorded by the engine. |
usage.est_input_tokens | integer | Estimated tokens in action plus context, used to choose the credit charge. |
usage.calls | array | Every upstream call, including retries. |
usage.calls[].stage | string | Stage for the call: stage_a or stage_b. |
usage.calls[].input_tokens | integer | Input-token count reported for this call. |
latency_ms | number | Recorded time in milliseconds; an example value is not a service guarantee. |
retention | string | The request-text retention notice, including the instruction never to send secrets. |
disclaimer | string | The reminder that Jevsus is an LLM experiment, not Jesus. |
Credits and pricing
Paid plans open shortly. Until then, a 402 lists Simple and Pro with available: false and no checkout_url, and checkout, /buy and the billing portal return 503 with the open-source option.
| Plan | Price | Credits |
|---|---|---|
Free | $0 | 10 credits once per key |
Simple | $10/month | 1,500 credits per billing period |
Pro | $30/month | 15,000 credits per billing period |
10 free credits; one credit is one decision.
Simple $10/month: 1,500 credits. Pro $30/month: 15,000 credits. Credits reset monthly and don't roll over.
Input up to 100K tokens: 1 credit; 100K to 1M tokens: 5 credits; over 1M: rejected, no charge.
WWJD estimates action + context as characters ÷ 4, rounded up, and reserves 1 or 5 credits. It then trues up from the model’s own token count: Stage B input tokens minus the estimate for instructions and Scripture. If that caller count exceeds 100K tokens after a 1-credit reserve, WWJD charges up to 4 more credits, capped at the key’s remaining balance. The returned credits.charged is the final charge.
Inputs above the model's working limit (about 600K tokens) are excerpted head and tail; basis.truncated is true. This can excerpt the action as well as the context; the whole original input is not judged. Input above 1M estimated tokens or a JSON body above 8 MB receives HTTP 413 with no charge. Every failed call is refunded, including an upstream context-length error, an exception, or a timeout. A model refusal is a valid answer and is charged; it returns discernment_required with refused and ask_human true.
Three kinds of HTTP 402
Every variant has error.code = payment_required and error.credits with plan, remaining, and needed.
- A free key has no credits left. The message is “Your free WWJD credits are used up. Paid plans open shortly; until then, run your own (open source).”
error.optionscontainsopen_source,simple, andpro. - A free key has credits, but fewer than the 5 this input needs. The message is “This input needs 5 credits; you have N. Paid plans open shortly; until then, run your own (open source).” Here N is the remaining balance,
credits.neededis 5, and the same three options are returned. - A Simple or Pro key has insufficient credits for this period. The following applies once paid plans open. The message gives its reset date.
credits.period_endcontains that date;error.optionscontains onlyopen_sourceandmanage.manage.urlopens the Stripe customer portal, where the human can manage the subscription or upgrade to Pro. There are no checkout links for this variant.
The open_source option includes the repository URL, AGPL-3.0 license, and self-hosting note. For free keys, simple and pro each include price_usd_month and credits_per_month. Both paid options have available: false and no checkout_url until paid plans open.
Show your human the returned options verbatim. Never create another key, edit or delete a key file, make a purchase, or set up self-hosting to get around a 402. Those choices belong to your human. After they say they paid, check the key’s plan and balance before retrying.
Checkout once paid plans open
The following checkout and subscription-management guidance applies once paid plans open.
Checkout links in a free-key 402 expire after seven days and never contain the secret API key. Opening GET /buy/<token>?plan=… shows a confirmation page with the plan, price, and key prefix. Only its button’s POST to the same URL creates a checkout session, followed by an HTTP 303 redirect. Staging redirects to a mock checkout; production redirects to Stripe.
If the payment link expired, ask the agent to run this command from its installed skill directory to get a new one:
node scripts/wwjd.mjs buy simple
To start checkout directly, send POST /v1/checkout with your bearer key and {"plan":"simple"} or {"plan":"pro"}. Open the returned url. You can also choose a plan on the home page.
A key with an active, trialing, or past-due subscription receives HTTP 409 already_subscribed with portal_url instead. Open that link to manage the existing subscription. After a purchase, keep using the same key.
Check your balance
node scripts/wwjd.mjs meOr call the API directly:
curl --fail-with-body "$WWJD_API_BASE/v1/me" \
-H "Authorization: Bearer $WWJD_API_KEY"
GET /v1/me returns key_id, the key’s non-secret prefix, plan, credits_remaining, period_end, created_at, and decisions_last_30d. API dates use ISO-8601 strings.
Manage billing once paid plans open
To manage a subscription, authenticate a GET /v1/billing/portal request and open its returned URL. A key without a Stripe customer receives HTTP 409.
Errors and request limits
Errors use {"error":{"code":"…","message":"…"}}, with extra fields when relevant. Keep the X-WWJD-Request-Id response header when reporting a problem. Every /v1/* response is JSON and uses Cache-Control: no-store.
| Error code | HTTP status | What to do |
|---|---|---|
invalid_request | 400 | Check the JSON body and required fields before trying again. |
unauthorized | 401 | Supply a valid WWJD key; create a free key if you do not have one. |
payment_required | 402 | Show the returned options to your human: open_source, simple, and pro for free keys; open_source and manage for paid keys. |
forbidden | 403 | This key is not allowed to perform the requested operation. |
not_found | 404 | Check the route or referenced resource. |
method_not_allowed | 405 | Use the HTTP method shown for the endpoint. |
already_subscribed | 409 | Checkout found an existing subscription. Open the returned portal_url to manage it instead of creating another subscription. |
input_too_large | 413 | Reduce the input to at most 1M estimated tokens and the JSON body to at most 8 MB. |
rate_limited | 429 | Wait for the Retry-After interval before trying again. |
upstream_error | 502 | The model provider failed; credits are refunded for this engine failure. |
service_unavailable | 503 | Try again later; honor Retry-After when present. |
upstream_timeout | 504 | The model provider timed out; credits are refunded for this engine failure. |
Decision requests are limited to 60 per key per minute; free keys are additionally limited to 10 per minute. Key creation is limited to five requests per minute, plus the daily cap described above. Challenge, checkout, payment-link, feedback, and health requests have a public limit of 60 per IP per minute. Release-list subscriptions have a separate limit of six per minute and 20 per day per IP tag. Respect Retry-After on a rate-limit response.
POST requests to the key, checkout, and email-subscription endpoints require Content-Type: application/json; a bodyless GET /v1/keys/challenge does not. In browsers they accept only the site’s own origin and do not provide cross-origin access. Command-line requests can call them without an Origin header.
A transport interruption can leave the outcome of a request unknown. Check your balance before repeatedly sending the same action.
Tell us about a wrong result
Send an authenticated POST /v1/feedback with the decision ID, the verdict you expected, and a note of at most 2,000 characters. The note follows the same 30-day text retention policy. This JSON shows the shape; replace the illustrative ID with the ID from your response:
{
"decision_id": "dec_illustrative_example",
"expected_verdict": "discernment_required",
"note": "Explain the missing context without including secrets or personal data."
}
Describe the action. Keep the secrets.
Request text is sent to OpenAI for evaluation and stored by WWJD for 30 days to improve WWJD, then deleted. Never send secrets. Do not include API keys, passwords, credentials, or personal data in action, context, or feedback. Describe the relevant situation instead.
Metadata is retained for analytics and evaluation. Anonymized rewritten examples may be added to our test set. Read the privacy notice or request deletion at saved@christlab.ai.
Run your own
The open-source WWJD repository is the launch destination for the skill, engine, Scripture corpus, and a self-host Worker under AGPL-3.0. Self-hosting uses your own OpenAI key and provider billing. Follow the repository’s setup instructions.
For tools and agents: OpenAPI specification · Agent-readable summary.