<@U04UQ68083H> Using the HTTP API, show an example...
# ory-copilot
c
@chilly-king-10285 Using the HTTP API, show an example using TyoeScript node.js of initiating a OTC for a passwordless login flow.
b
A message from kapa.ai
Certainly! To initiate a one-time code (OTC) passwordless login flow using the Ory HTTP API in a TypeScript Node.js environment, you need to: 1. Create a login flow (using the native or browser endpoint). 2. Submit the identifier (e.g., email) to trigger the OTC flow. Below is a TypeScript example using the Ory SDK (
@ory/client
) to initiate the flow. This example demonstrates how to create a native login flow and submit the identifier to start the OTC process.
Copy code
typescript import { Configuration, FrontendApi } from "@ory/client"  const frontend = new FrontendApi(   new Configuration({     basePath: `https://${process.env.ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`,   }), )  async function initiateOtcLogin(email: string) {   // 1. Create a native login flow   const { data: flow } = await frontend.createNativeLoginFlow()    // 2. Submit the identifier to trigger the OTC (one-time code) method   await frontend.updateLoginFlow({     flow: flow.id,     updateLoginFlowBody: {       method: "code", // "code" is the method for OTC       identifier: email,     },   })    // At this point, Ory will send a one-time code to the provided email. }  initiateOtcLogin("<mailto:user@example.com|user@example.com>").catch(console.error)
- Replace
process.env.ORY_PROJECT_SLUG
with your actual Ory project slug or set it in your environment. - The
method: "code"
field is used to indicate the OTC passwordless method. - The user will receive a code via email, which they must enter in the next step to complete authentication. This flow is based on the documented process for enabling and using one-time code passwordless login in Ory Network, and the usage of the Ory SDK for Node.js/TypeScript to interact with the HTTP API [Passwordless login](https://www.ory.sh/docs/identities/get-started/passwordless) [Create login flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-login-flow).
c
@chilly-king-10285 Don’t use the SDK, use axios instead
b
A message from kapa.ai
Certainly! Here’s how you can initiate a passwordless one-time code (OTC) login flow using the Ory HTTP API with axios in TypeScript/Node.js, based strictly on the documented flow: 1. Create a native login flow by sending a GET request to
/self-service/login/api
. 2. Submit the identifier (e.g., email) and specify the
code
method to trigger the OTC process. Below is a TypeScript example using axios:
Copy code
typescript import axios from "axios"  const ORY_PROJECT_SLUG = process.env.ORY_PROJECT_SLUG // Set your Ory project slug const BASE_URL = `https://${ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`  async function initiateOtcLogin(email: string) {   // 1. Create a native login flow   const flowRes = await axios.get(`${BASE_URL}/self-service/login/api`)   const flow = flowRes.data    // 2. Submit the identifier to trigger the OTC (one-time code) method   await <http://axios.post|axios.post>(     flow.ui.action,     {       method: "code",      // Use the "code" method for OTC       identifier: email,   // The user's email address     },     {       headers: { "Content-Type": "application/json" },     }   )    // At this point, Ory will send a one-time code to the provided email. }  initiateOtcLogin("<mailto:user@example.com|user@example.com>").catch(console.error)
- The first request creates a login flow and retrieves the
ui.action
URL for the next step. - The second request posts the identifier and method to the
ui.action
endpoint, which triggers the sending of the one-time code to the user’s email. This approach is directly based on the Ory documentation for creating and submitting login flows using the HTTP API, and the structure of the flow object as shown in the docs [Create a flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-a-flow) and [Create login flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-login-flow).
c
@chilly-king-10285 Now show a example of the API to call to attain a session with the OTC.
b
A message from kapa.ai
To complete the passwordless login flow using a one-time code (OTC) with the Ory HTTP API, you need to submit the code received by the user to the login flow. This is done by making a POST request to the
ui.action
URL from the login flow, including the code and the method. Here’s an example using axios in TypeScript/Node.js:
Copy code
typescript import axios from "axios"  const ORY_PROJECT_SLUG = process.env.ORY_PROJECT_SLUG // Set your Ory project slug const BASE_URL = `https://${ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`  async function completeOtcLogin(flowId: string, code: string) {   // 1. Fetch the login flow to get the ui.action URL   const flowRes = await axios.get(`${BASE_URL}/self-service/login/flows?id=${flowId}`)   const flow = flowRes.data    // 2. Submit the code to attain a session   const response = await <http://axios.post|axios.post>(     flow.ui.action,     {       method: "code", // Use the "code" method for OTC       code: code,     // The one-time code received by the user     },     {       headers: { "Content-Type": "application/json" },       // You may need to include cookies if required by your flow     }   )    // If successful, the response will include the session information or set a session cookie   return response.data }  // Usage example completeOtcLogin("your-login-flow-id", "123456").then(console.log).catch(console.error)
How it works: - The user receives a code via email or SMS after initiating the OTC login. - You fetch the login flow (using its ID) to get the correct
ui.action
endpoint. - You POST the code and method to the
ui.action
endpoint. - If the code is valid, Ory will create a session for the user. This flow is described in the Ory documentation for one-time code authentication and custom UI integrations. The exact field names and endpoints are confirmed in the docs, but the knowledge sources do not provide a full HTTP request/response example for this step. The above example is constructed based on the documented flow and field names for submitting the code to the login flow endpoint [Email and SMS one-time-code authentication](https://www.ory.sh/docs/kratos/passwordless/one-time-code) and [Custom UI basic integration](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#session-checks). If you need to check the session after login, you can use the
/sessions/whoami
endpoint as described in the docs [Ory API Reference](https://www.ory.sh/docs/reference/api).
c
@chilly-king-10285 Why is this code producing a 400 BAD_REQUEST ERROR: async function initiateOTCLogin(email: string) { // 1. Create a native login flow const flowRes = await axios.get(
${ORY_BASE_URL}/self-service/login/api
) const flow = flowRes.data console.log(flow) // 2. Submit the identifier to trigger the OTC (one-time code) method const otc_login_result = await axios.post( flow.ui.action, { method: “code”, // Use the “code” method for OTC identifier: email, // The user’s email address }, { headers: { “Content-Type”: “application/json” }, } ) console.log(JSON.stringify(otc_login_result, null, 3)) return otc_login_result }
b
A message from kapa.ai
Your code is producing a
400 Bad Request
error when submitting the identifier to trigger the OTC (one-time code) login. Based on the Ory documentation and community discussions, here are the most likely reasons: ### 1. Incorrect Quotes Your code uses “smart quotes” (
and
) instead of standard ASCII quotes (
"
). This will cause a syntax error in JavaScript/TypeScript and may result in an invalid JSON payload being sent to the Ory API, which would trigger a 400 error. Fix:
Copy code
typescript {   method: "code",      // Use the "code" method for OTC   identifier: email,   // The user’s email address }
### 2. Missing Required Fields For the code method, the Ory API expects at least the
method
and
csrf_token
fields in some flows (especially browser flows). For native flows, CSRF may not be required, but if the flow expects it and it's missing, you will get a 400 error. Double-check the flow type and required fields in the
flow.ui.nodes
array. ### 3. Incorrect Endpoint or Flow State If the flow is expired, already used, or the endpoint is not correct (e.g., using a browser flow endpoint for an API flow), you will get a 400 error. Always use the
ui.action
URL from the flow object you just fetched. ### 4. Payload Validation A 400 error is returned if the payload is malformed or missing required fields. The Ory API expects the payload to match the expected schema for the login method. For the code method, the required fields are typically
method
and
identifier
(and possibly
csrf_token
for browser flows) [see API reference](https://www.ory.sh/docs/reference/api). ### 5. Flow Already Completed or Invalid If you try to reuse a flow that has already been completed or is expired, Ory will return a 400 error. Always use a fresh flow for each login attempt. --- #### Example of a Correct Request (with standard quotes):
Copy code
typescript const otc_login_result = await <http://axios.post|axios.post>(   flow.ui.action,   {     method: "code",     identifier: email,     // csrf_token: "..." // Only if required, check flow.ui.nodes for this field   },   {     headers: {       "Content-Type": "application/json"     },   } )
--- #### Troubleshooting Steps - Check the response body of the 400 error. Ory provides detailed error messages in the response body, which can help pinpoint the exact issue. - Inspect the flow object (
console.log(flow)
) to see if there are any required fields (like
csrf_token
) in the
ui.nodes
. - Ensure you are using the correct endpoint (
flow.ui.action
) and that the flow is fresh. --- #### Reference This behavior is consistent with Ory's documented API and community feedback, where 400 errors are returned for invalid or incomplete payloads, missing required fields, or expired/invalid flows. Developers have reported similar confusion when the API returns 400 for what appears to be a valid request, but the underlying issue is usually a missing or malformed field, or an expired flow [see discussion](https://github.com/ory/kratos/issues/4052) and [form submission and payload validation](https://www.ory.sh/docs/kratos/self-service#form-submission-and-payload-validation-2). If you continue to see 400 errors, check the error message in the response body for more details.
c
@chilly-king-10285 What does the value of the
csrf
token need to be, and where do I obtain it?
b
A message from kapa.ai
The value of the
csrf_token
must match the token provided by Ory in the flow object when you initiate a self-service flow (such as login, registration, recovery, or verification). You obtain the
csrf_token
from the
ui.nodes
array in the flow response, where it appears as a hidden input field named
csrf_token
. For example, after you create a login flow, the response will include something like:
Copy code
json {   "ui": {     "nodes": [       {         "type": "input",         "group": "default",         "attributes": {           "name": "csrf_token",           "type": "hidden",           "value": "XmG3qwTYSV0oWIyNGTugvtNOKMxWPYHd7dNX7BYK5lL79P0iUdq5jVmRUKwwm8RLcAGN7eF7iYraAiTSOdamuQ==",           "required": true,           "disabled": false         },         "messages": [],         "meta": {}       }     ]   } }
You must extract the value of the
csrf_token
from this node and include it in your subsequent POST request to submit the flow. The token is unique per flow and must not be made up or reused from another flow. This is required for browser flows to protect against cross-site request forgery attacks. The browser will also send a CSRF cookie automatically, but you must manually include the token value in your request body as shown above [see documentation](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#debug) [see form rendering](https://www.ory.sh/docs/kratos/self-service#form-rendering). In summary: - The
csrf_token
value is provided by Ory in the flow response. - You must extract it from the flow's
ui.nodes
and include it in your form or API request when submitting the flow.
c
@kep
@chilly-king-10285 There is no value in the csrf node return by the initiation of the OTC login flow: { “type”: “input”, “group”: “default”, “attributes”: { “name”: “csrf_token”, “type”: “hidden”, “value”: “”, “required”: true, “disabled”: false, “node_type”: “input” }, “messages”: [], “meta”: {} },
b
A message from kapa.ai
When you initiate a login flow and the
csrf_token
node in the response has an empty value (i.e.,
"value": ""
), this is expected behavior for API (native) flows in Ory Kratos. According to the Ory documentation and quickstart examples, the CSRF token is only required and populated for browser flows. For API/native flows, the CSRF token is not used, and the value will be empty. You do not need to include a CSRF token when submitting the flow in this case [see quickstart example](https://www.ory.sh/docs/kratos/quickstart#perform-registration-login-and-logout). Here’s an example from the documentation:
Copy code
json {   "type": "input",   "group": "default",   "attributes": {     "name": "csrf_token",     "type": "hidden",     "value": "",     "required": true,     "disabled": false,     "node_type": "input"   },   "messages": [],   "meta": {} }
So, if you are using the native (API) flow for OTC login, you can safely omit the
csrf_token
from your submission payload, or send it as an empty string. This is consistent with the documented behavior and examples provided by Ory [see quickstart](https://www.ory.sh/docs/kratos/quickstart#perform-registration-login-and-logout). If you are using a browser flow and the value is still empty, that would be unexpected and could indicate a misconfiguration, but for API flows, this is correct and you do not need to worry about the CSRF token.
c
@chilly-king-10285 What is the URL for the “native API flow” to initiate an OTC Login?
b
A message from kapa.ai
The URL to initiate a native API flow for an OTC (one-time code) login in Ory is:
Copy code
GET /self-service/login/api
You should send a GET request to this endpoint on your Ory project base URL. This endpoint is specifically for native applications (such as mobile apps or other non-browser clients) and is the correct way to start a login flow that can use the OTC method. For example:
Copy code
https://<your-project-slug>.<http://projects.oryapis.com/self-service/login/api|projects.oryapis.com/self-service/login/api>
This is confirmed in the Ory documentation for native login flows and is the correct starting point for an OTC login in a native app context [see Ory API Reference](https://www.ory.sh/docs/reference/api) and [custom UI integration basics](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-login-flow).
c
@chilly-king-10285 What is the URL for the browser version
b
A message from kapa.ai
The URL to initiate a browser login flow in Ory is:
Copy code
GET /self-service/login/browser
You should send a GET request to this endpoint on your Ory project base URL. For example:
Copy code
https://<your-project-slug>.<http://projects.oryapis.com/self-service/login/browser|projects.oryapis.com/self-service/login/browser>
This endpoint is specifically for browser-based applications and will set the appropriate cookies and anti-CSRF measures required for browser flows. This is confirmed in the Ory documentation for creating a browser login flow [Create login flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-login-flow) and [API Reference](https://www.ory.sh/docs/kratos/reference/api).
c
@chilly-king-10285 Why am I getting this 400 error when I hit the REST HTTP API for
<https://bold-kepler-liove18x32.projects.oryapis.com/self-service/login?flow=3b0f2eab-66de-4618-96d5-6c3ffcb6a5d6>
Copy code
{
  "message": "Request failed with status code 400",
  "name": "AxiosError",
  "stack": "AxiosError: Request failed with status code 400\n    at settle (webpack-internal:///(rsc)/./node_modules/axios/lib/core/settle.js:24:12)\n    at IncomingMessage.handleStreamEnd (webpack-internal:///(rsc)/./node_modules/axios/lib/adapters/http.js:629:71)\n    at IncomingMessage.emit (node:events:531:35)\n    at endReadableNT (node:internal/streams/readable:1696:12)\n    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)\n    at Axios.request (webpack-internal:///(rsc)/./node_modules/axios/lib/core/Axios.js:57:41)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async initiateOTCLogin (webpack-internal:///(rsc)/./src/collections/subscriptions/subscribe/subscribe.ts:98:30)\n    at async subscribe (webpack-internal:///(rsc)/./src/collections/subscriptions/subscribe/subscribe.ts:183:30)\n    at async handleEndpoints (file:///Users/ajb/Server/IMG/img-api/node_modules/payload/dist/utilities/handleEndpoints.js:168:26)\n    at async eval (webpack-internal:///(rsc)/./node_modules/@payloadcms/next/dist/routes/rest/index.js:30:20)\n    at async <http://AppRouteRouteModule.do|AppRouteRouteModule.do> (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:26:33891)\n    at async AppRouteRouteModule.handle (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:26:41254)\n    at async doRender (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/base-server.js:1513:42)\n    at async DevServer.renderToResponseWithComponentsImpl (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/base-server.js:1915:28)\n    at async DevServer.renderPageComponent (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/base-server.js:2393:24)\n    at async DevServer.renderToResponseImpl (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/base-server.js:2430:32)\n    at async DevServer.pipeImpl (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/base-server.js:1003:25)\n    at async NextNodeServer.handleCatchallRenderRequest (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/next-server.js:304:17)\n    at async DevServer.handleRequestImpl (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/base-server.js:895:17)\n    at async /Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/dev/next-dev-server.js:371:20\n    at async Span.traceAsyncFn (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/trace/trace.js:157:20)\n    at async DevServer.handleRequest (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/dev/next-dev-server.js:368:24)\n    at async invokeRender (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/lib/router-server.js:247:21)\n    at async handleRequest (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/lib/router-server.js:438:24)\n    at async requestHandlerImpl (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/lib/router-server.js:462:13)\n    at async Server.requestListener (/Users/ajb/Server/IMG/img-api/node_modules/next/dist/server/lib/start-server.js:158:13)",
  "config": {
    "transitional": {
      "silentJSONParsing": true,
      "forcedJSONParsing": true,
      "clarifyTimeoutError": false
    },
    "adapter": [
      "xhr",
      "http",
      "fetch"
    ],
    "transformRequest": [
      null
    ],
    "transformResponse": [
      null
    ],
    "timeout": 0,
    "xsrfCookieName": "XSRF-TOKEN",
    "xsrfHeaderName": "X-XSRF-TOKEN",
    "maxContentLength": -1,
    "maxBodyLength": -1,
    "env": {},
    "headers": {
      "Accept": "application/json, text/plain, */*",
      "Content-Type": "application/json",
      "Authentication": "Bearer <valid ory api token here>",
      "User-Agent": "axios/1.8.4",
      "Content-Length": "155",
      "Accept-Encoding": "gzip, compress, deflate, br"
    },
    "method": "post",
    "url": "<https://bold-kepler-liove18x32.projects.oryapis.com/self-service/login?flow=3b0f2eab-66de-4618-96d5-6c3ffcb6a5d6>",
    "data": "{\"method\":\"code\",\"identifier\":\"<mailto:ajb+test1@visia.ca|ajb+test1@visia.ca>\",\"csrf_token\":\"Zn1tqBDdYt+8RtqNVVSADqaSJbUhD9Y8THYPk91i6gN4tE7/cVzKLrrvdqng5HmTA6z7aJtTzQQwrzSV35xEQw==\"}",
    "allowAbsoluteUrls": true
  },
  "code": "ERR_BAD_REQUEST",
  "status": 400
}
b
A message from kapa.ai
Based on the information in your error and the Ory documentation, here are the most likely reasons for receiving a
400 Bad Request
when calling `https://bold-kepler-liove18x32.projects.oryapis.com/self-service/login?flow=3b0f2eab-66de-4618-96d5-6c3ffcb6a5d6`: --- ### 1. Incorrect Use of API vs. Browser Flow You are using the
/self-service/login?flow=...
endpoint, which is intended for browser flows. For API/native flows, you should use the
/self-service/login/api
endpoint to initiate the flow and then submit to the
ui.action
URL provided in the flow object. Submitting to the wrong endpoint or mixing up browser and API flows can result in a 400 error [see API reference](https://www.ory.sh/docs/kratos/reference/api). --- ### 2. CSRF Token Issues For browser flows, the CSRF token is required and must be valid. If the CSRF token is missing, empty, or does not match what Ory expects, you will get a 400 error. For API/native flows, the CSRF token is not required and is typically empty. If you are using a browser flow, ensure you are sending the correct CSRF token as provided in the flow's
ui.nodes
. If the value is empty, that may indicate you are using an API flow, in which case you should not include the CSRF token [see CSRF troubleshooting](https://www.ory.sh/docs/troubleshooting/csrf). --- ### 3. Flow Expiry or Invalid Flow If the flow has expired or is no longer valid (for example, if it has already been used or is too old), Ory will return a 400 error. Always use a fresh flow for each login attempt. The error response body from Ory will usually include an
error.id
such as
self_service_flow_expired
if this is the case [see API reference](https://www.ory.sh/docs/kratos/reference/api). --- ### 4. Malformed or Unexpected Payload If the payload does not match what Ory expects for the flow and method, you will get a 400 error. Double-check that you are sending the correct fields (
method
,
identifier
, and
csrf_token
if required) and that their values are correct. Also, ensure you are not sending extra or malformed fields. --- ### 5. Authentication Header The
Authentication
header is not standard for Ory public self-service endpoints. Unless you are calling an admin or private endpoint, you should not need to send an
Authentication
header. For self-service flows, authentication is handled via cookies and CSRF tokens, not bearer tokens. --- ### What to Do Next - Check the error response body from Ory for more details. It often contains an
error.id
and a message explaining the problem. - Ensure you are using the correct flow type and endpoint (browser vs. API). - Use the
ui.action
URL
from the flow object for your POST request, not the generic
/self-service/login?flow=...
unless you are following the browser flow pattern. - Double-check the CSRF token: For browser flows, it must be present and valid; for API flows, it should be omitted or empty. - Do not send an
Authentication
header
unless specifically required by the endpoint. --- If you need more specific help, please provide the full error response body from Ory (not just the Axios error), as it will contain the exact reason for the 400 error. --- This answer is based on the Ory documentation and real-world issues reported by other developers using Ory Kratos [see API reference](https://www.ory.sh/docs/kratos/reference/api) [see CSRF troubleshooting](https://www.ory.sh/docs/troubleshooting/csrf) [see GitHub issue discussion](https://github.com/ory/kratos/issues/4052).
c
@chilly-king-10285 I’m sending a request to
/self-service/login/api
to initiate an OTC passwordless login, and it’s creating the OTC, and sending the email, but the response I’m getting from the API request is a 400 error, why?
b
A message from kapa.ai
c
{ id: ‘64e72d58-ee4f-4d79-bb43-5f019ee32a0c’, organization_id: null, type: ‘api’, expires_at: ‘2025-06-14T071412.038613Z’, issued_at: ‘2025-06-14T064412.038613Z’, request_url: ‘https://bold-kepler-liove18x32.projects.oryapis.com/self-service/login/api’, active: ‘code’, ui: { action: ‘https://bold-kepler-liove18x32.projects.oryapis.com/self-service/login?flow=64e72d58-ee4f-4d79-bb43-5f019ee32a0c’, method: ‘POST’, nodes: [ [Object], [Object], [Object], [Object], [Object] ], messages: [ [Object] ] }, created_at: ‘2025-06-14T064412.044532Z’, updated_at: ‘2025-06-14T064412.044532Z’, refresh: false, requested_aal: ‘aal1’, state: ‘sent_email’, transient_payload: {} }
b
When you initiate an OTC (one-time code) passwordless login by sending a request to
/self-service/login/api
, and the code is created and the email is sent, but you receive a 400 error in the API response, this is a known and documented behavior in Ory Kratos. Why does this happen? - Ory currently returns a
400 Bad Request
status code for incomplete flows, even if the request is valid and the next step (such as sending the OTC email) was successfully triggered. This means that even though the email is sent, the API responds with 400, which can be confusing because it suggests a client error when, in fact, the process is working as intended. - This behavior is acknowledged by the Ory team and discussed in detail in [this GitHub issue](https://github.com/ory/kratos/issues/4052). The rationale is that Ory only returns
200 OK
when the login flow is fully completed (i.e., the user is authenticated and a session is created). For intermediate steps—such as sending an OTC email—Ory returns a non-success status code (like 400) to indicate the flow is not yet finished. Key points from the discussion: - The 400 status does not necessarily mean your request was malformed; it can also mean the flow is not yet complete. - The response body will contain information about the next step, such as a message indicating that an email with a code has been sent. - This behavior is planned to be improved in a future major version, where more granular and appropriate status codes (like 202 for "accepted, processing") may be used, but for now, 400 is expected for these intermediate steps. What should you do? - Parse the response body for messages or instructions, rather than relying solely on the HTTP status code to determine if the step was successful. - If you see a message in the response like "An email containing a code has been sent to the email address you provided," you can proceed to the next step in the flow, even if the status code is 400. References from the knowledge sources: - "We will still return a non-success code for any form fill that is incomplete or being processed. 200 OK will only ever be sent when the flow is successfully completed." - "400 response, in this case, sends a wrong signal that the server could not process the request even though it successfully did. ... The issue is that we talk to the API and the API gives us misleading status codes, so we can't rely on the status codes to detect whether our request was successful or not, which is the very point of status codes, like you normally do for any HTTP API that properly implements status codes." - "This behavior is expected. 200 is only sent on successful login." [Requesting a login code returns HTTP 400](https://github.com/ory/kratos/issues/4052) In summary: A 400 error after initiating an OTC login is expected in the current Ory Kratos implementation. Check the response body for confirmation that the code was sent, and proceed with the next step of the login flow.