WeWeb + Xano Sign Up Page

PLEASE help me with a signup workflow tutorial?

I’ve connected the XanoAuth plugin to WeWeb, and I’ve already created a successful login page with a working login workflow. But Now I need help on creating the a signup page.

I’ve created the sign up form container and I’ve binded each field (name, email, password) to the respective variable. My Xano already has a signup/auth API endpoint.

What should my workflow look like on WeWeb?

(Should I edit any information in the Xano workflow, or leave it?)

New to Xano and WeWeb, any help would be greatly appreciated <3

Hi Matt :waving_hand:

If you’re using the Xano Auth plugin in WeWeb, you should have a signup form available here:

That signup form comes with a pre-built workflow:

You need to bind the variables in the “Sign up” action and choose which page to redirect to after signup in the “Navigate to” action.

And that’s it. Assuming you’ve finished configuring the Xano Auth plugin in WeWeb and are using the default email + password endpoint in Xano, it should work out of the box.

If that’s not working for you, it would help if you shared screenshots or a short video of how things are setup in both WeWeb and Xano.

Hi Joyce, Thank you so much for your prompt response.

Here is the error message. .

Hey Matt,

Thanks for sharing this. The error helps, but doesn’t give enough visibility into how things are set up between Xano and WeWeb to pinpoint the issue :slight_smile:

To help debug this properly, could you share a bit more context?

What would be most helpful:

  • A screenshot of the full error response
    Right now, we only see the top-level fields (name, message, status, etc.). There’s usually more detail under response → data → message that tells us exactly what Xano is rejecting.
    You can check this here: https://docs.weweb.io/plugins/auth-systems/xano-auth.html#troubleshooting-xano-authentication

  • A screenshot of your signup endpoint in Xano
    (including the input parameters)

  • A screenshot of your Xano Auth plugin setup in WeWeb
    (so we can confirm the correct signup endpoint is selected)

  • A screenshot of your form in WeWeb
    (to see what inputs you’re collecting and how they’re bound)

  • A screenshot of your workflow actions
    (especially what data is being sent to Xano)

The goal is to understand exactly what’s being sent vs. what Xano expects.

Right now, based on the 400 error, we can make some educated guesses (like missing required inputs, wrong endpoint, or trying to create a user that already exists), but we can’t be sure.

If you can share those screenshots, we should be able to narrow this down quickly :+1:

Hi Joyce,

I’ve attached screenshots below. (I have to post them 1-by-1 as an error message comes up because I’m a new member. I also edited each link in the text below by removing the “ps” in “https” because it wouldn’t allow this post to have too many links)

I haven’t made any changes to the API end-points in Xano Authentication API group (maybe I should be making changes there?)

Below is the full error message (it’s super long)

name: “AxiosError”
stack: “AxiosError: Request failed with status code 400 at iWe (htt://editor-cdn.weweb.io/assets/index-Vjj6SXa_.js:1262:1094) at XMLHttpRequest.y (htt://editor-cdn.weweb.io/assets/index-Vjj6SXa_.j…”
message: “Request failed with status code 400”
code: “ERR_BAD_REQUEST”
status: 400
config
timeout: 0
xsrfCookieName: “XSRF-TOKEN”
xsrfHeaderName: “X-XSRF-TOKEN”
maxContentLength: -1
maxBodyLength: -1
validateStatus: function(e){return e>=200&&e<300}
method: “post”
data: “{“name”:”“,“email”:”“}”
withCredentials: false
url: “htt://x8ki-letl-twmt.n7.xano.io/api:prYnh__P/auth/signup”
allowAbsoluteUrls: true
transitional
silentJSONParsing: true
forcedJSONParsing: true
clarifyTimeoutError: false
adapter
0: “xhr”
1: “http”
2: “fetch”
transformRequest
0: function(e,t){const n=t.getContentType()||“”,s=n.indexOf(“application/json”)>-1,r=ii.isObject(e);if(r&&ii.isHTMLForm(e)&&(e=new FormData(e)),ii.isFormData(e))return s?JSON.stringify(eWe(e)):e;if(ii.isArrayBuffer(e)||ii.isBuffer(e)||ii.isStream(e)||ii.isFile(e)||ii.isBlob(e)||ii.isReadableStream(e))return e;if(ii.isArrayBufferView(e))return e.buffer;if(ii.isURLSearchParams(e))return t.setContentType(“application/x-www-form-urlencoded;charset=utf-8”,!1),e.toString();let a;if(r){if(n.indexOf(“application/x-www-form-urlencoded”)>-1)return rvt(e,this.formSerializer).toString();if((a=ii.isFileList(e))||n.indexOf(“multipart/form-data”)>-1){const l=this.env&&this.env.FormData;return jZ(a?{“files”:e}:e,l&&new l,this.formSerializer)}}return r||s?(t.setContentType(“application/json”,!1),lvt(e)):e}
transformResponse
0: function(e){const t=this.transitional||UB.transitional,n=t&&t.forcedJSONParsing,s=this.responseType===“json”;if(ii.isResponse(e)||ii.isReadableStream(e))return e;if(e&&ii.isString(e)&&(n&&!this.responseType||s)){const o=!(t&&t.silentJSONParsing)&&s;try{return JSON.parse(e,this.parseReviver)}catch(a){if(o)throw a.name===“SyntaxError”?Vs.from(a,Vs.ERR_BAD_RESPONSE,this,null,this.response):a}}return e}
env
FormData: function FormData() { [native code] }
Blob: function Blob() { [native code] }
headers
Accept: “application/json, text/plain, /
Content-Type: “application/json”
request
onreadystatechange: null
readyState: 4
timeout: 0
withCredentials: false
responseURL: “htt://x8ki-letl-twmt.n7.xano.io/api:prYnh__P/auth/signup”
status: 400
statusText: “”
responseType: “”
response: “{“code”:“ERROR_CODE_INPUT_ERROR”,“message”:“Missing param: field_value”,“payload”:{“param”:“field_value”}}”
responseText: “{“code”:“ERROR_CODE_INPUT_ERROR”,“message”:“Missing param: field_value”,“payload”:{“param”:“field_value”}}”
UNSENT: 0
OPENED: 1
HEADERS_RECEIVED: 2
LOADING: 3
DONE: 4
abort: function abort() { [native code] }
getAllResponseHeaders: function getAllResponseHeaders() { [native code] }
getResponseHeader: function getResponseHeader() { [native code] }
open: function open() { [native code] }
overrideMimeType: function overrideMimeType() { [native code] }
send: function send() { [native code] }
setRequestHeader: function setRequestHeader() { [native code] }
responseXML: null
setAttributionReporting: function setAttributionReporting() { [native code] }
setPrivateToken: function setPrivateToken() { [native code] }
onloadstart: null
onprogress: null
onabort: function(){m&&(n(new Vs(“Request aborted”,Vs.ECONNABORTED,i,m)),m=null)}
onerror: function(w){const v=w&&w.message?w.message:“Network Error”,S=new Vs(v,Vs.ERR_NETWORK,i,m);S.event=w||null,n(S),m=null}
onload: null
ontimeout: function(){let w=s.timeout?“timeout of “+s.timeout+“ms exceeded”:“timeout exceeded”;const v=s.transitional||JUe;s.timeoutErrorMessage&&(w=s.timeoutErrorMessage),n(new Vs(w,v.clarifyTimeoutError?Vs.ETIMEDOUT:Vs.ECONNABORTED,i,m)),m=null}
onloadend: function y(){if(!m)return;const A=Cf.from(“getAllResponseHeaders"in m&&m.getAllResponseHeaders()),v={data:!a||a===“text”||a===“json”?m.responseText:m.response,status:m.status,statusText:m.statusText,headers:A,config:i,request:m};iWe(function(x){t(x),g()},function(x){n(x),g()},v),m=null}
addEventListener: function addEventListener() { [native code] }
dispatchEvent: function dispatchEvent() { [native code] }
removeEventListener: function removeEventListener() { [native code] }
when: function when() { [native code] }
upload
onloadstart: null
onprogress: null
onabort: null
onerror: null
onload: null
ontimeout: null
onloadend: null
addEventListener: function addEventListener() { [native code] }
dispatchEvent: function dispatchEvent() { [native code] }
removeEventListener: function removeEventListener() { [native code] }
when: function when() { [native code] }
response
status: 400
statusText: “”
data
code: “ERROR_CODE_INPUT_ERROR”
message: “Missing param: field_value”
payload
param: “field_value”
headers
cache-control: “private, no-cache, no-store, must-revalidate”
content-type: “application/json; charset=UTF-8”
expires: “Tue, 05 Apr 2016 00:43:26 GMT”
pragma: “no-cache”
config
timeout: 0
xsrfCookieName: “XSRF-TOKEN”
xsrfHeaderName: “X-XSRF-TOKEN”
maxContentLength: -1
maxBodyLength: -1
validateStatus: function(e){return e>=200&&e<300}
method: “post”
data: “{“name”:””,“email”:””}"
withCredentials: false
url: https:x8ki-letl-twmt.n7.xano.io/api:prYnh__P/auth/signup
allowAbsoluteUrls: true
transitional
silentJSONParsing: true
forcedJSONParsing: true
clarifyTimeoutError: false
adapter
0: “xhr”
1: “http”
2: “fetch”
transformRequest
0: function(e,t){const n=t.getContentType()||“”,s=n.indexOf(“application/json”)>-1,r=ii.isObject(e);if(r&&ii.isHTMLForm(e)&&(e=new FormData(e)),ii.isFormData(e))return s?JSON.stringify(eWe(e)):e;if(ii.isArrayBuffer(e)||ii.isBuffer(e)||ii.isStream(e)||ii.isFile(e)||ii.isBlob(e)||ii.isReadableStream(e))return e;if(ii.isArrayBufferView(e))return e.buffer;if(ii.isURLSearchParams(e))return t.setContentType(“application/x-www-form-urlencoded;charset=utf-8”,!1),e.toString();let a;if(r){if(n.indexOf(“application/x-www-form-urlencoded”)>-1)return rvt(e,this.formSerializer).toString();if((a=ii.isFileList(e))||n.indexOf(“multipart/form-data”)>-1){const l=this.env&&this.env.FormData;return jZ(a?{“files”:e}:e,l&&new l,this.formSerializer)}}return r||s?(t.setContentType(“application/json”,!1),lvt(e)):e}
transformResponse
0: function(e){const t=this.transitional||UB.transitional,n=t&&t.forcedJSONParsing,s=this.responseType===“json”;if(ii.isResponse(e)||ii.isReadableStream(e))return e;if(e&&ii.isString(e)&&(n&&!this.responseType||s)){const o=!(t&&t.silentJSONParsing)&&s;try{return JSON.parse(e,this.parseReviver)}catch(a){if(o)throw a.name===“SyntaxError”?Vs.from(a,Vs.ERR_BAD_RESPONSE,this,null,this.response):a}}return e}
env
FormData: function FormData() { [native code] }
Blob: function Blob() { [native code] }
headers
Accept: “application/json, text/plain, /
Content-Type: “application/json”
request
onreadystatechange: null
readyState: 4
timeout: 0
withCredentials: false
responseURL: “htt://x8ki-letl-twmt.n7.xano.io/api:prYnh__P/auth/signup”
status: 400
statusText: “”
responseType: “”
response: “{“code”:“ERROR_CODE_INPUT_ERROR”,“message”:“Missing param: field_value”,“payload”:{“param”:“field_value”}}”
responseText: “{“code”:“ERROR_CODE_INPUT_ERROR”,“message”:“Missing param: field_value”,“payload”:{“param”:“field_value”}}”
UNSENT: 0
OPENED: 1
HEADERS_RECEIVED: 2
LOADING: 3
DONE: 4
abort: function abort() { [native code] }
getAllResponseHeaders: function getAllResponseHeaders() { [native code] }
getResponseHeader: function getResponseHeader() { [native code] }
open: function open() { [native code] }
overrideMimeType: function overrideMimeType() { [native code] }
send: function send() { [native code] }
setRequestHeader: function setRequestHeader() { [native code] }
responseXML: null
setAttributionReporting: function setAttributionReporting() { [native code] }
setPrivateToken: function setPrivateToken() { [native code] }
onloadstart: null
onprogress: null
onabort: function(){m&&(n(new Vs(“Request aborted”,Vs.ECONNABORTED,i,m)),m=null)}
onerror: function(w){const v=w&&w.message?w.message:“Network Error”,S=new Vs(v,Vs.ERR_NETWORK,i,m);S.event=w||null,n(S),m=null}
onload: null
ontimeout: function(){let w=s.timeout?"timeout of "+s.timeout+“ms exceeded”:“timeout exceeded”;const v=s.transitional||JUe;s.timeoutErrorMessage&&(w=s.timeoutErrorMessage),n(new Vs(w,v.clarifyTimeoutError?Vs.ETIMEDOUT:Vs.ECONNABORTED,i,m)),m=null}
onloadend: function y(){if(!m)return;const A=Cf.from("getAllResponseHeaders"in m&&m.getAllResponseHeaders()),v={data:!a||a===“text”||a===“json”?m.responseText:m.response,status:m.status,statusText:m.statusText,headers:A,config:i,request:m};iWe(function(x){t(x),g()},function(x){n(x),g()},v),m=null}
addEventListener: function addEventListener() { [native code] }
dispatchEvent: function dispatchEvent() { [native code] }
removeEventListener: function removeEventListener() { [native code] }
when: function when() { [native code] }
upload
onloadstart: null
onprogress: null
onabort: null
onerror: null
onload: null
ontimeout: null
onloadend: null
addEventListener: function addEventListener() { [native code] }
dispatchEvent: function dispatchEvent() { [native code] }
removeEventListener: function removeEventListener() { [native code] }
when: function when() { [native code] }

Thanks Matt, I think I see what’s going on :slight_smile:

I had a look at your project and the error changed from “Missing param: field_value” (what you shared above) to “Missing param: name” (the error currently being returned when testing your project). This suggests the Xano endpoint has been modified between tests. In other words, what the API expects is currently inconsistent.

To move forward, let’s simplify and verify the source of truth:

1. Test directly in Xano
Open your /auth/signup endpoint and use the “Run” button with this payload:

{
  "name": "test",
  "email": "test@test.com",
  "password": "123456"
}

If this fails, the issue is with the endpoint configuration in Xano.
If it succeeds, then Xano is correctly set up.

2. Keep the endpoint unchanged
At this stage, make sure to publish the latest changes made to the endpoint and avoid modifying the Xano endpoint further so we can debug against a stable setup.

3. Align WeWeb with Xano
Ensure your WeWeb request body matches the working payload exactly:

  • name

  • email

  • password
    All fields should be present and non-empty.

Right now, Xano is rejecting the request because it’s not receiving all expected fields, and that expectation has been shifting. Stabilizing the endpoint and matching it precisely will resolve that.

Hi Joyce, Thank you so much for the help. It’s working now. I think it’s because I was using an already registered email address :slight_smile: I re-did the input fields (name,email,password) in the Xano API, published the changes, used a different email address in the WeWeb field, and it’s working :slight_smile:

Since your login flow already works, I’d check the signup endpoint contract before changing the WeWeb side too much. The built-in Xano Auth signup action is pretty opinionated around the default Xano auth endpoint, so a 400 usually means Xano is expecting different field names or extra required inputs than the plugin is sending. The fastest test is to make the signup endpoint accept only email + password first and get that working end to end. Then add name or other profile fields in a second request after signup succeeds, instead of trying to make the auth endpoint do everything at once.