webium / silverstripe-indeed-apply
Indeed Apply integration for SilverStripe - receive job applications via Indeed's Quick Apply feature
Installs: 19
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Type:silverstripe-vendormodule
pkg:composer/webium/silverstripe-indeed-apply
Requires
- php: >=7.4
- silverstripe/cms: ^4.0 || ^5.0
README
This module enables receiving job applications from Indeed Apply via a POST endpoint.
What does this module do?
This module:
- Provides an endpoint (
/indeed-apply) where Indeed Apply applications can be sent - Logs all incoming requests for debugging
- Stores applications in the database for further processing
- Provides a CMS interface for managing applications and logs
Installation
- Install via Composer:
composer require webium/silverstripe-indeed-apply
- Run dev/build to create database tables:
vendor/bin/sake dev/build flush=1
Database Tables
After installation, the following tables are created:
IndeedApply- Contains the applicationsIndeedApplyLog- Contains logs of all requests
Endpoint
The module creates a POST endpoint at:
https://yourdomain.com/indeed-apply
Customizing the Endpoint URL
You can customize the endpoint URL by editing your project's config file:
app/_config/indeed-apply-custom.yml:
--- Name: indeed-apply-custom After: 'indeed-apply-config' --- # Custom route configuration SilverStripe\Control\Director: rules: 'jobs/apply': 'Webium\IndeedApply\Controllers\IndeedApplyController' # Update the url_segment to match Webium\IndeedApply\Controllers\IndeedApplyController: url_segment: 'jobs/apply'
Important: Don't forget to update your Indeed Apply postUrl in your job feed to match the new endpoint:
https://yourdomain.com/jobs/apply
Run /dev/build?flush=1 after changing the route.
Authentication
The module supports HMAC-SHA1 signature verification for authenticating Indeed Apply POST requests.
Configuration
Add the following to your project's .env file:
# Indeed Apply Module Configuration INDEED_APPLY_API_SECRET="your-api-secret-here" INDEED_APPLY_REQUIRE_SIGNATURE="false"
Environment Variables:
INDEED_APPLY_API_SECRET- Your shared API secret from Indeed Apply integration settingsINDEED_APPLY_REQUIRE_SIGNATURE- Set to"true"to require valid signatures (recommended for production)
The controller reads these environment variables directly using Environment::getEnv().
How it works
- Indeed Apply sends a POST request with an
x-indeed-signatureheader - The signature is generated using HMAC-SHA1 on the raw JSON body with your shared API secret
- The module verifies the signature matches the expected value
- All requests are logged with signature validation status in
IndeedApplyLog
Security Options
Development mode (default):
INDEED_APPLY_REQUIRE_SIGNATURE="false"
- All requests are accepted regardless of signature
- Signature validation status is logged for debugging
- Use this for initial testing
Production mode (recommended):
INDEED_APPLY_REQUIRE_SIGNATURE="true"
- Only requests with valid signatures are accepted
- Invalid signatures return HTTP 401 Unauthorized
- Protects against unauthorized POST requests
Getting Your API Secret
- Log in to your Indeed Employer account
- Go to Indeed Apply integration settings
- Find the "Shared API Secret" with credential type "Indeed Apply"
- Copy the secret and add it to your configuration
XML Feed Configuration
Add the following field to your Indeed XML feed:
<indeed-apply-data><![CDATA[indeed-apply-apiToken=YOUR_API_TOKEN&indeed-apply-jobTitle=JOB_TITLE&indeed-apply-jobId=JOB_ID&indeed-apply-jobCompanyName=COMPANY_NAME&indeed-apply-jobLocation=LOCATION&indeed-apply-jobUrl=https%3A%2F%2Fyourdomain.com%2Fjob-url&indeed-apply-postUrl=https%3A%2F%2Fyourdomain.com%2Findeed-apply%2F]]></indeed-apply-data>
Note: The indeed-apply-postUrl must point to your domain's /indeed-apply/ endpoint
Expected Data
The module expects the following fields from Indeed:
Job Information
jobTitle- Job titlejobId- Job/vacancy IDjobCompanyName- Company namejobLocation- LocationjobUrl- URL to job posting
Candidate Information
fullName- Full namefirstName- First namelastName- Last nameemail- Email addressphoneNumber- Phone numbercoverLetter- Cover letterresume- CV (file upload)
Custom Questions
All fields starting with question_ or customQuestion are automatically stored as JSON.
CMS Interface
After installation, a new menu item appears in the CMS: Indeed Apply
Here you can:
-
Indeed Apply Applications - View and manage applications
- All received applications
- Candidate information
- CV downloads
- Track status (processed/not processed)
- Add notes
-
Indeed Apply Logs - View request logs
- All received requests
- Request headers and body
- Response codes
- Error messages
- Logs are read-only
Permissions
The module uses the following permission:
CMS_ACCESS_IndeedApplyAdmin- Access to Indeed Apply administration
This can be assigned to user groups via Security > Groups.
Processing Applications
Applications are stored with IsProcessed = false. You can later:
- Manually process them in the CMS
- Automatically process them via a custom script
- Export to Excel for bulk processing
Debugging
All requests are logged in IndeedApplyLog. Here you can see:
- When a request came in
- What the request body was
- Signature validation status (true/false)
- Whether it was successful
- Any error messages
The SignatureValid field in logs shows whether the HMAC-SHA1 signature was valid for each request.
Technical Details
- Namespace:
Webium\IndeedApply - Endpoint URL:
/indeed-apply - Accepted methods: POST only
- CSRF Protection: Disabled for this endpoint (required for external POST requests)
- File uploads: Max 5MB, allowed extensions: pdf, doc, docx, txt, rtf
Response Codes
The endpoint returns the following HTTP status codes:
| Code | Description |
|---|---|
| 200 | Application received successfully |
| 401 | Invalid signature (when INDEED_APPLY_REQUIRE_SIGNATURE is enabled) |
| 405 | Method not allowed (only POST is accepted) |
| 409 | Duplicate application: candidate has already applied for this job |
| 410 | Job does not exist or is no longer available (via extension hook) |
Duplicate Application Check (409)
The module automatically checks for duplicate applications based on CandidateEmail and JobId. If a candidate has already applied for the same job, the endpoint returns HTTP 409.
Extension Hooks
The controller provides extension hooks for custom validation logic.
validateJobId Hook
Use this hook to validate whether a JobId exists in your ATS (Applicant Tracking System). If the job doesn't exist, return a 410 response.
Create an extension in your application:
// app/src/Extension/IndeedApplyJobValidator.php namespace App\Extension; use SilverStripe\Core\Extension; class IndeedApplyJobValidator extends Extension { public function validateJobId(string $jobId, ?string &$error): void { // Check against your ATS $jobExists = MyATSService::jobExists($jobId); if (!$jobExists) { $error = "Job {$jobId} does not exist or is no longer available"; } } }
Register in app/_config/config.yml:
Webium\IndeedApply\Controllers\IndeedApplyController: extensions: - App\Extension\IndeedApplyJobValidator
Run dev/build flush=1 after adding the extension.
Resume File Security
Resume files are uploaded to Uploads/IndeedApply/Resumes/ and are automatically protected. Each uploaded resume has CanViewType set to LoggedInUsers, ensuring that only logged-in CMS users can access the files.
Troubleshooting
"Method Not Allowed" error
Check if Indeed is actually sending POST requests to the endpoint.
Applications not being saved
- Check the logs in
Indeed Apply Logsin the CMS - Look at the
RequestBodyto see what data Indeed is sending - Verify that field names match
CVs not uploading
- Check file upload settings in
php.ini - Verify that the
Uploads/IndeedApply/Resumesdirectory has write permissions - Check logs for specific error messages
Invalid signature errors (HTTP 401)
- Verify your
INDEED_APPLY_API_SECRETin.envmatches the shared secret in your Indeed Apply integration settings - Check the
SignatureValidfield inIndeedApplyLogto see signature validation status - Ensure
INDEED_APPLY_REQUIRE_SIGNATUREis set to"false"during initial testing - Confirm the
X-Indeed-Signatureheader is being sent by Indeed
Requirements
- SilverStripe 4.x or 5.x
Documentation
For more information about Indeed Apply, see: https://docs.indeed.com/indeed-apply/add-indeed-apply
License
MIT
Maintainer
Webium - https://webium.nl