POST
Client.Login
Alias of Customer.Login. Same customer portal login as client.
Permissions
- This function can be called without a bearer token.
Action
- Runs the Client.Login function and returns JSON.
Push Service
No push is sent because this function only reads data or returns helper information.
Automation
No automation is run for this function.
Special Instructions
- This is NOT staff Login. Staff use /app/Login. Customers use this route.
- On an org subdomain (https://user.bull36.com/app/Customer.Login) you do not need user_name. The org is loaded from user_detail.user_domain = first host label ("user"), same as the dashboard hidden user_name field.
- On that subdomain send only client_id (or email) + password. Then otp on the second call.
- On shared domains (bull36.com with no org subdomain) send user_name as the organization user id or username, plus client_id or email, plus password.
- On a custom org domain the org is loaded from user_detail.domain = Host. You may still send domain_name.
- Password is checked against contactus.password like the dashboard.
- Dashboard always emails OTP after a valid password. Send otp on the second call.
- The returned token is auth_type=client. Staff routes reject it.
- After login, call Customer.Welcome then Customer.Tickets.List, Customer.Projects.List, Customer.Invoices.List, Customer.Orders.List, Customer.Entries.FormData / List, Customer.DynamicContent.List, Customer.Products.List, Customer.Appointments.Doctors, Customer.Appointments.Types, Customer.Appointments.List, Customer.Appointments.Add, Customer.Appointments.Coupons, Customer.Appointments.Coupon.Add / Edit / Delete, Customer.Files.List, Customer.Rooms.List. Those routes auto-scope to token c_id.
- Alias: Client.Login.
Required Parameters
No body parameters are required for this function.
Optional Parameters
| Name | Type | Sample | What it gives |
|---|---|---|---|
user_namepost | string | 24552 | Organization user id or username. Not required on an org subdomain (user.bull36.com); that host is matched to user_detail.user_domain. Required on shared bull36.com. Aliases: username, org_id. |
domain_namepost | string | shop.example.com | Organization domain when user_name is omitted and Host is not an org subdomain. On custom domains Host is used automatically. Alias: domain. |
client_idpost | string | 12614595 | Customer id (contactus.id). If not numeric, treated as email. Aliases: customer_id, c_id, email. |
emailpost | string | [email protected] | Customer email when client_id is not the numeric id. |
passwordpost | string | CUSTOMER PASSWORD | Required unless token is sent. contactus.password, same as dashboard. |
otppost | string | 123456 | 6-digit email OTP from step 1. Alias: email_otp. |
Sample Request
{
"url": "\/app\/Client.Login",
"method": "POST",
"headers": [],
"body": {
"client_id": "12614595",
"password": "CUSTOMER PASSWORD"
},
"url_user": "https:\/\/{user}.bull36.com\/app\/Client.Login",
"url_domain": "https:\/\/{domain}\/app\/Client.Login"
}Endpoint
POST /app/Client.Login
POST https://{user}.bull36.com/app/Client.Login
POST https://{domain}/app/Client.LoginSample Output
{
"success": "3",
"otp_required": true,
"message": "OTP send in your email...!",
"expires_in": 600
}JavaScript Example
const domain = 'https://user.bull36.com';
async function customerLogin({ client_id, password, otp, user_name }) {
const body = new URLSearchParams({ client_id, password });
if (user_name) body.set('user_name', user_name);
if (otp) body.set('otp', otp);
const res = await fetch(`${domain}/app/Customer.Login`, { method: 'POST', body });
const data = await res.json();
if (data.otp_required) return { otpRequired: true, message: data.message, data };
if (data.token) return { token: data.token, tokenType: data.token_type, expiresAt: data.expires_at, customer: data.customer, redirectTo: data.redirect_to };
throw new Error(data.message || 'Customer.Login failed');
}Error Example
{
"success": "0",
"message": "Login Failed Your Email Or Password Was Incorrect Please Try Again"
}