Clear next steps.
- Day-to-day Team members know the next action immediately.
- Operations Fewer complaints stay blocked in inboxes.
- Business Lower public escalation and better customer trust.
Most teams do not lose customers because of one complaint. They lose them because complaints get stuck, ownership is unclear, and replies arrive too late.
SolveClaims fixes that with a strict flow, clear owners, and visible timing at every stage, so your team works faster without cutting quality.
Onboarding is designed to be short and safe. The sequence is fixed so identity and organization data are verified from day one.
After submission, the organization enters PENDING_APPROVAL. You get access after validation.
A retail team finishes all 3 onboarding steps in one session, then invites agents only after approval.
The owner completes onboarding, then creates admin users later, once internal responsibility is clear.
In Dashboard -> Settings, owners configure how the team works every day.
Roles are there to keep decisions clear and prevent accidental misuse.
| Role | Main responsibility | Typical actions |
|---|---|---|
| Owner | Business control and governance | Approvals, policy updates, API keys, team governance |
| Admin | Daily coordination | Approvals, supervision, assignment, operational unblock |
| Agent | Execution | Pick up claims, draft proposals, close solved work |
| Collaborator | Support contribution | Operational help inside granted visibility |
| Member | Limited participation | Work on assigned items with constrained scope |
This is the operational route each complaint follows.
| Status | What it means | What should happen next |
|---|---|---|
NEW |
Fresh complaint, not yet owned | Agent/Admin/Owner picks it up |
IN_PROGRESS |
Claim is actively worked on | Classify tags, draft solution, prepare next step |
WAITING_FOR_APPROVAL |
Internal review pending | Admin/Owner approves or sends back for adjustment |
WAITING_CLIENT_SIGNATURE |
Proposal sent to client | Client confirms or case auto-closes on timeout |
PROCESSING_SOLUTION |
Client accepted | Team executes the promised solution |
SOLVED |
Execution complete | Case is operationally resolved |
CLOSED |
Closed without client confirmation | Owner/Admin can reopen to IN_PROGRESS when needed |
Actions are intentionally sequenced. You do not skip steps.
NEW to IN_PROGRESS.
IN_PROGRESS to WAITING_FOR_APPROVAL.
WAITING_CLIENT_SIGNATURE.
PROCESSING_SOLUTION.
PROCESSING_SOLUTION to SOLVED.
WAITING_CLIENT_SIGNATURE are system-driven, which
protects consistency and auditability.
These are the features teams use most often to keep the complaint pipeline healthy.
Tasks turn long complaint threads into concrete next actions.
NEW (triage lock).
"Collect signed replacement form by 16:00" assigned to logistics specialist.
"Issue partial refund and attach proof" with due date and owner.
SLA is your timing policy. It gives every phase a clear target.
| SLA stage | Measured while status is | Operational meaning |
|---|---|---|
| First response SLA | NEW |
How fast a new complaint is picked up |
| Classification SLA | IN_PROGRESS |
How fast required tags are assigned after pickup |
| Approval SLA | WAITING_FOR_APPROVAL |
How fast internal decision is made |
| Client response SLA | WAITING_CLIENT_SIGNATURE |
How long client has to respond |
| Resolution SLA | PROCESSING_SOLUTION |
How fast the team executes after client acceptance |
If first response SLA is 2h and a claim is created at 10:00, pickup should happen by 12:00 to stay healthy.
Use coverage transfer when a teammate is unavailable (planned leave or sudden absence) so active work keeps moving without SLA disruption.
The API lane is for teams that want to import complaints from external websites or apps.
Endpoint family in scope: POST https://app.solve.claims/api/v1/ingest.
app.solve.claims with an Owner account.Dashboard -> Settings -> API Keys.Endpoint: POST https://app.solve.claims/api/v1/ingest
Headers:
Content-Type: application/json
x-api-key: sc_live_...
Body sample:
{
"clientName": "Jane Doe",
"clientEmail": "jane@example.com",
"clientPhone": "+40 700 000 000",
"reference": "ORDER-1001",
"description": "Complaint details",
"files": [
{
"name": "invoice.pdf",
"type": "application/pdf",
"size": 245760
}
]
}
image/jpeg, image/png, image/webp, application/pdf429 Too Many Requests; respect Retry-AftercomplaintId + uploads[] signed URLs.PUT to the signed URLs.{
"success": true,
"complaintId": "cm123...",
"uploads": [
{
"fileName": "invoice.pdf",
"fileType": "application/pdf",
"uploadUrl": "https://...signed-url..."
}
]
}
INGEST_ALLOWED_ORIGINS.429 is returned.PENDING: scan running, download blocked.CLEAN: download allowed.MALICIOUS: quarantined.FAILED: blocked until resolved.stored_gb = complaints_per_month * avg_files_per_complaint * avg_file_size_mb / 1024
storage_cost = stored_gb * storage_rate_per_gb
put_cost = (total_upload_requests / 1000) * put_rate
get_cost = (total_download_requests / 1000) * get_rate
import express from "express";
import fetch from "node-fetch";
const app = express();
app.use(express.json({ limit: "2mb" }));
app.post("/api/complaints/submit", async (req, res) => {
const payload = {
clientName: req.body.clientName,
clientEmail: req.body.clientEmail || "",
clientPhone: req.body.clientPhone || "",
reference: req.body.reference || "",
description: req.body.description,
files: req.body.files || []
};
const ingestResponse = await fetch("https://app.solve.claims/api/v1/ingest", {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": process.env.SOLVECLAIMS_API_KEY
},
body: JSON.stringify(payload)
});
const ingestData = await ingestResponse.json();
if (!ingestResponse.ok) {
return res.status(ingestResponse.status).json(ingestData);
}
return res.json(ingestData);
});
add_action('admin_post_nopriv_sc_submit_complaint', 'sc_submit_complaint');
add_action('admin_post_sc_submit_complaint', 'sc_submit_complaint');
function sc_submit_complaint() {
$payload = array(
'clientName' => sanitize_text_field($_POST['clientName'] ?? ''),
'clientEmail' => sanitize_email($_POST['clientEmail'] ?? ''),
'reference' => sanitize_text_field($_POST['reference'] ?? ''),
'description' => sanitize_textarea_field($_POST['description'] ?? ''),
'files' => array(),
);
$response = wp_remote_post('https://app.solve.claims/api/v1/ingest', array(
'headers' => array(
'Content-Type' => 'application/json',
'x-api-key' => getenv('SOLVECLAIMS_API_KEY'),
),
'body' => wp_json_encode($payload),
'timeout' => 20,
));
wp_redirect(home_url('/complaint-thank-you/'));
exit;
}
<?php
$payload = [
"clientName" => $_POST["clientName"],
"clientEmail" => $_POST["clientEmail"] ?? "",
"reference" => $_POST["reference"] ?? "",
"description" => $_POST["description"],
"files" => []
];
$ch = curl_init("https://app.solve.claims/api/v1/ingest");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"x-api-key: " . getenv("SOLVECLAIMS_API_KEY")
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
?>
Technically yes, but that exposes your API key. Production best practice is always server-side relay.
No. Policy allows only JPEG, PNG, WEBP, and PDF in ingest.
They appear in Dashboard -> Claims with NEW status and enter normal workflow.
Comments and mentions without noise
Comments are split by communication purpose.
Mention rules (plain language)
CLIENT_MESSAGE.