Agency & Freelance template
Review a client's retainer hours and priorities every month
Import a Laravel retainer check-in form for Filament to record monthly hours, client satisfaction, priorities, and renewal risk.
Updated
This Laravel retainer check-in form gives an agency client a regular place to report hours used, satisfaction, and current priorities. Import it into FilaForms to collect one monthly check-in, then review the stored submission beside the client work your Laravel application already manages.
The field table below supplies the imported structure for the conversation. Each completed check-in records the hours used, satisfaction response, and priorities as a submission in your application's database. Your account manager can review the response before the next monthly planning call.
Fields in this template (14 fields, 8 required)
Field types used: Text, Email, Date, Number, Currency, Radio, Textarea, Toggle Buttons. Every field is editable in the builder after import.
Account
| Field | Type | Required | Validation |
|---|---|---|---|
Client Name
client_name
|
Text | Yes |
required |
Contact Email
email
|
Yes |
required | email |
|
Reporting Month
reporting_month
|
Date | Yes |
required |
Account Manager
account_manager
|
Text | No | none |
This Month
| Field | Type | Required | Validation |
|---|---|---|---|
Hours Used
hours_used
|
Number | Yes |
required | numeric | min:0 |
Hours Included
hours_included
|
Number | Yes |
required | numeric | min:0 |
Overage Charge
overage
|
Currency | No | none |
Satisfaction This Month
satisfaction
|
Radio, 4 options | Yes |
required |
What Went Well
went_well
|
Textarea | No | none |
What Should Change
should_change
|
Textarea | No | none |
Next Month
| Field | Type | Required | Validation |
|---|---|---|---|
Priorities
priorities
|
Textarea | Yes |
required |
Renew Retainer
renew
|
Toggle Buttons, 4 options | Yes |
required |
Adjusted Monthly Hours
adjusted_hours
|
Number | No |
numeric | min:0 |
Notes
notes
|
Textarea | No | none |
Turn a monthly response into an account action
The field table below groups operational review details before future planning. Start with hours used because it frames the client's view of the month. Follow it with satisfaction and priorities, because raw capacity figures cannot explain whether the work served the client's current needs.
Keep the monthly cadence consistent when you want to compare responses over time. A client may report low usage because they lacked requests, because delivery moved slowly, or because the agreed scope changed. The priorities response gives the account manager context before they draw a conclusion from the hours alone.
Use the settings block to decide who receives the form and when. Send the request before a scheduled monthly review, not after the month has already closed. That timing lets the client name upcoming work while the team still has time to adjust the next period's plan.
The submission dashboard is useful for reviewing individual answers. Your application should own the client relationship rules that follow. It may match a submission to an account record, add a review task, or notify the person responsible for the renewal conversation.
Flag a client before renewal risk becomes a surprise
A low satisfaction response deserves a deliberate follow-up. Treat it as a signal for an account manager to review the complete response, rather than as an automatic decision about the client relationship. The priorities value can reveal a missed expectation, a new business need, or a request that belongs in a separate scope discussion.
The listener below reads hours_used, satisfaction, and priorities from this template. It records the answers and marks recognised low satisfaction values in the application log. Replace that log destination with your existing account-review queue or notification workflow after you define who owns the follow-up.
Save the listener as app/Listeners/LogRetainerCheckIn.php. Laravel 12 discovers listeners in app/Listeners automatically.
<?php
declare(strict_types=1);
namespace App\Listeners;
use FilaForms\Core\Events\FormSubmitted;
use Illuminate\Support\Facades\Log;
final class LogRetainerCheckIn
{
public function handle(FormSubmitted $event): void
{
$submission = $event->submission;
$data = $submission->data;
$satisfaction = $data['satisfaction'] ?? null;
Log::info('Retainer check-in submitted', [
'form_id' => $event->form->getKey(),
'submission_id' => $submission->getKey(),
'hours_used' => $data['hours_used'] ?? null,
'satisfaction' => $satisfaction,
'priorities' => $data['priorities'] ?? null,
'needs_account_review' => in_array($satisfaction, ['low', '1', '2'], true),
]);
}
}
Read the log entry as a triage record, not as a client-health score. An account manager should see the full submission before contacting the client. They can confirm whether the response reflects a one-off delivery issue, an expected quiet period, or a genuine concern about the value of the retainer.
If your application already has account ownership, route low satisfaction to that owner. Include the submitted priorities in the review task so the recipient can prepare a useful response. Keep escalation rules outside the form when they depend on contract value, renewal timing, or a client's history.
Adapt the check-in for fixed monthly scope
For retainers with a fixed monthly scope, drop the adjusted_hours field. A fixed-scope arrangement measures whether agreed work happened, not whether hours were rebalanced. Removing that field keeps the check-in aligned with the agreement and avoids asking clients to interpret a number they do not use.
Add a renewal_date field when the account team needs contract timing beside each monthly response. That value helps the team see which conversations need to move from routine planning into renewal preparation. Make the field relevant to an existing contract record when possible, so your application has one source of truth for the term.
Do not use a changed form structure to rewrite earlier submissions. A check-in from a prior period should retain the questions the client saw then. New fields can begin with the next reporting cycle, while the saved response remains an accurate record of that month's conversation.
Use check-ins as a renewal signal, not an invoice
Monthly check-ins work best when the account manager reviews patterns across several submissions. Repeated low satisfaction, unclear priorities, and a widening gap between planned work and reported usage can justify an earlier conversation. A single answer can start that review, but it should not replace judgment from the team that knows the account.
Use this template alongside a Laravel form builder for Filament when the check-in belongs in the client portal your agency operates. Review the FilaForms features for Laravel and Filament when you need to connect submissions to notifications, analytics, or the rest of your application workflow.
This form does not calculate overage billing on its own. The listener only stores the answers, so invoicing still happens in a separate system. Keep billing calculations in the system that owns rates, approved time, invoice status, and financial records.
Know when another workflow fits better
Choose this template when a monthly account review needs a structured client response before a planning or renewal discussion. It gives the account manager a repeatable input surface and keeps the original response available for later review. Use a separate workflow when the primary job is calculating charges or issuing invoices.
The template does not decide whether a client should renew. It gives your team timely evidence for that decision. That boundary matters. An account manager may need to compare the submitted response with project delivery, open requests, contract terms, and conversations that a form cannot capture.
Use this template in your own application
With FilaForms installed, one call creates the form with every field, rule, and setting listed above. Then edit it in the builder.
use FilaForms\Core\Support\FormTemplates;
$form = FormTemplates::createFormFromTemplate('retainer-check-in');
Template schema (JSON)
{
"name": "Retainer Check-In",
"description": "Run a monthly retainer review with hours and priorities",
"icon": "heroicon-o-arrow-trending-up",
"category": "Agency & Freelance",
"popularity": "Medium",
"sections": [
{
"name": "Account",
"fields": [
{
"label": "Client Name",
"type": "text",
"code": "client_name",
"required": true,
"width": "50"
},
{
"label": "Contact Email",
"type": "email",
"code": "email",
"required": true,
"validation": [
{
"name": "email",
"parameters": []
}
],
"width": "50"
},
{
"label": "Reporting Month",
"type": "date",
"code": "reporting_month",
"required": true,
"width": "50"
},
{
"label": "Account Manager",
"type": "text",
"code": "account_manager",
"required": false,
"width": "50"
}
]
},
{
"name": "This Month",
"fields": [
{
"label": "Hours Used",
"type": "number",
"code": "hours_used",
"required": true,
"validation": [
{
"name": "numeric",
"parameters": []
},
{
"name": "min",
"parameters": [
"0"
]
}
],
"width": "50"
},
{
"label": "Hours Included",
"type": "number",
"code": "hours_included",
"required": true,
"validation": [
{
"name": "numeric",
"parameters": []
},
{
"name": "min",
"parameters": [
"0"
]
}
],
"width": "50"
},
{
"label": "Overage Charge",
"type": "currency",
"code": "overage",
"required": false,
"width": "50"
},
{
"label": "Satisfaction This Month",
"type": "radio",
"code": "satisfaction",
"required": true,
"options": [
{
"value": "Very satisfied"
},
{
"value": "Satisfied"
},
{
"value": "Neutral"
},
{
"value": "Dissatisfied"
}
],
"width": "50"
},
{
"label": "What Went Well",
"type": "textarea",
"code": "went_well",
"required": false,
"width": "100"
},
{
"label": "What Should Change",
"type": "textarea",
"code": "should_change",
"required": false,
"width": "100"
}
]
},
{
"name": "Next Month",
"fields": [
{
"label": "Priorities",
"type": "textarea",
"code": "priorities",
"required": true,
"width": "100"
},
{
"label": "Renew Retainer",
"type": "toggle-buttons",
"code": "renew",
"required": true,
"options": [
{
"value": "Yes"
},
{
"value": "Adjust hours"
},
{
"value": "Pause"
},
{
"value": "Cancel"
}
],
"width": "100"
},
{
"label": "Adjusted Monthly Hours",
"type": "number",
"code": "adjusted_hours",
"required": false,
"validation": [
{
"name": "numeric",
"parameters": []
},
{
"name": "min",
"parameters": [
"0"
]
}
],
"width": "50"
},
{
"label": "Notes",
"type": "textarea",
"code": "notes",
"required": false,
"width": "50"
}
]
}
]
}
Frequently asked questions
- What does this Laravel retainer check-in form record?
- It records the hours used, satisfaction response, and priorities for one monthly retainer check-in as a submission in your application's database.
- Can I use this form for a fixed-scope retainer?
- Yes. Drop the adjusted_hours field when the retainer has a fixed monthly scope.
- How can I flag a client who may not renew?
- Listen for FilaForms\\Core\\Events\\FormSubmitted, read hours_used, satisfaction, and priorities from the submission data, then route a low satisfaction response to the account manager.
- Does this form calculate retainer overage billing?
- No. The form does not calculate overage billing on its own. The listener only stores the answers, so invoicing still happens in a separate system.