Display the Enhanced Job Posting panel

The Enhanced Job Posting panel displays a job posting form with all fields required to post a SEEK job ad pre-filled with details from a job requisition provided by your software. It allows hirers to review and edit the pre-filled form before posting.The panel provides an event your software can trigger which will save the hirer’s job ad as a draft. This draft can then be queried, previewed, or posted to SEEK.

Before you begin

Review the getting started documentation if you’re new to panels in the SEEK API.Enhanced Job Posting follows our standard browser support policy.

Step 1: Include the panel

Add the following script tag to the page where you want to insert the panel for Enhanced Job Posting:
HTML
Copy
<script
  type="text/javascript"
  src="https://integration.seek.com/panels/SeekApi.js"
></script>
This will expose a SeekApi.render function which renders the Enhanced Job Posting panel.

Step 2: Render the panel

The render function must be called on page load and whenever the properties of the job requisition change. For example, if the hirer updates the job title of the requisition, you must re-render the panel to reflect changes.
JavaScript
Copy
const { dispatchEvent } = SeekApi.render(
  containerNode,
  'enhancedJobPosting',
  props
);
SeekApi.render will return an object containing the dispatchEvent function, which you can use to trigger the draft to be saved. See manage drafts for further details.We recommend your containerNode spans the full width of your UI for the best user experience.

Props

Prop
Type
Description
getAuthToken
() => Promise<string>
Function to retrieve a browser token for the SEEK API
position
object
The position object containing job requisition details which will be used to pre-fill the panel
draftPositionProfileId
stringoptional
If the user has previously created a draft with Enhanced Job Posting and a draftPositionProfileId has been returned, the id should be provided whenever the panel is rerendered.
positionProfileId
stringoptional
An existing job ad to be edited. When supplied, the panel will open in edit mode for that job ad. When omitted, the panel will open in create mode for a new job ad.
locale
stringoptional
Specifies the locale to display content in, e.g. en-AU. Set this prop to override the default localisation behaviour based on users browser preferences. Supported locales are outlined in the content localisation documentation.

position prop

The position prop aligns to the structure of the PostPositionInput type used in classic Job Posting, with differences to accommodate the Enhanced Job Posting panel:
  • Many mandatory fields from PostPositionInput are optional in the position prop. When omitted the fields will be inferred or defaulted by the panel for the hirer to edit.
  • Validation to optional fields will not return errors to your system or prevent the panel from displaying. Instead, the panel will use defaults or otherwise allow the hirer to resolve. For example:
    • Where an input violates maximum / minimum lengths, the panel will attempt to display the input with a validation error.
    • Where fields expecting set values are supplied with unrecognised values, a default option will be inferred or displayed as not selected.
  • positionLocation should be provided as a formatted address (for example, 60 Cremorne St, Cremorne VIC 3121) and country code (for example, AU) which the SEEK location will be inferred from.
  • A SEEK job category will be inferred from the provided position values if an OID is not provided in the jobCategories argument.
  • ATS Screening Questions can be passed directly into the panel to create a questionnaire when the job is posted. If omitted an interactive form will be provided to allow the hirer to add their own questions.

Example

JavaScript
Copy
const { dispatchEvent } = SeekApi.render(
  document.getElementById('seekPanelContainer'),
  'enhancedJobPosting',
  {
    getAuthToken: async () => {
      // Do not implement caching in your `getAuthToken` implementation.
      // The panel will internally memoise the response.
      const token = await fetchAuthToken();
      return token;
    },

    position: {
      positionOpening: {
        postingRequester: {
          roleCode: 'Company',
          // Hirer that will own the position opening
          id: 'seekAnzPublicTest:organization:seek:93WyyF1h',
          // Mary Manager is hiring this role as a hiring manager
          personContacts: [
            {
              communication: {
                email: [{ address: 'mary.manager@example.com' }],
                phone: [{ formattedNumber: '03 8517 4100' }],
                address: [
                  {
                    extendedLines: [{ type: 'Apartment', value: 'line 1' }],
                    line: 'line 2',
                    countrySubDivisions: [{ type: 'State', value: 'Vic' }],
                    geoLocation: { latitude: 0, longitude: 0 },
                    formattedAddress: 'line 3',
                    countryCode: 'AU',
                    postalCode: '1234',
                    city: 'Melbourne',
                  },
                ],
              },
              name: {
                family: 'Manager',
                given: 'Mary',
                formattedName: 'Mary Manager',
              },
              roleCode: 'HiringManager',
            }
          ]
        },
        statusCode: 'Active',
      },
      basePositionProfile: {
        positionTitle: 'Associate Developer',
        // Hirer to post the job ad on behalf of
        positionOrganizations: ['seekAnzPublicTest:organization:seek:93WyyF1h'],
        // The locations provided are currently used to infer a single SEEK location
        positionLocation: [
          {
            address: {
              formattedAddress: '1 Cremorne St, Bendigo, VIC 3550',
              countryCode: 'AU',
            },
          },
        ],
        seekAnzWorkTypeCode: 'PartTime',
        seekWorkArrangementCodes: ['Hybrid'],
        // 70—85 Australian dollars per hour
        offeredRemunerationPackage: {
          basisCode: 'Hourly',
          // No remuneration description will be displayed to the candidate
          descriptions: [],
          ranges: [
            {
              minimumAmount: {
                value: 70,
                currency: 'AUD',
              },
              maximumAmount: {
                value: 85,
                currency: 'AUD',
              },
              intervalCode: 'Hour',
            },
          ],
        },
        positionFormattedDescriptions: [
          {
            descriptionId: 'AdvertisementDetails',
            content: 'A newly created role for an ambitious <b>junior</b> or <b>graduate</b> developer. Be part of a great team in the Bendigo area.',
          },
          {
            descriptionId: 'SearchSummary',
            content: 'Start your software development career with a dynamic & growing team',
          },
          {
            descriptionId: 'SearchBulletPoint',
            content: 'bullet point 1',
          },
          {
            descriptionId: 'SearchBulletPoint',
            content: 'bullet point 2',
          },
          {
            descriptionId: 'SearchBulletPoint',
            content: 'bullet point 3',
          },
        ],
        postingInstructions: [
          {
            applicationMethods: [
              { applicationUri: { url: 'https://example.com/applyform' } },
            ]
          },
        ],
        seekBillingReference: 'billing reference',
        seekPartnerMetadata: 'partner metadata',
        seekHirerJobReference: 'job reference',

        // Must be YouTube link
        seekVideo: 'https://www.youtube.com/watch?v=iM9onXRkTa8j'

        // Application questions can be passed in from your system to create a read-only view for
        // the hirer. Alternatively you can provide an interactiveQuestionnaire: {} to allow the
        // hirer to add their own questions.

        seekApplicationQuestionnaire: {
          seekApplicationQuestionnaire: [
            {
              componentTypeCode: 'Question',
              question: {
                componentTypeCode: 'Question',
                questionHtml: 'What do you like most about GraphQL?',
                value: 'question-value-0',
                responseTypeCode: 'FreeText',
              },
            },
            {
              componentTypeCode: 'Question',
              question: {
                componentTypeCode: 'Question',
                questionHtml:
                  'Which of the following statements best describes your right to work in Australia?',
                value: 'question-value-1',
                responseTypeCode: 'SingleSelect',
                responseChoice: [
                  {
                    text: "I'm an Australian citizen",
                    value: '14970',
                    preferredIndicator: true,
                  },
                  {
                    text: "I'm a permanent resident and/or NZ citizen",
                    value: '14971',
                    preferredIndicator: true,
                  },
                ],
              },
            },
            {
              componentTypeCode: 'Question',
              question: {
                componentTypeCode: 'Question',
                questionHtml:
                  'Which of the following forklift licences do you have?',
                responseTypeCode: 'MultiSelect',
                responseChoice: [
                  {
                    text: 'Forklift truck (LF)',
                    preferredIndicator: true,
                  },
                  {
                    text: 'Order picking forklift truck (LO)',
                    preferredIndicator: true,
                  },
                  {
                    text: 'None of these',
                    preferredIndicator: false,
                  },
                ],
              },
            },
            {
              componentTypeCode: 'PrivacyConsent',
              privacyConsent: {
                componentTypeCode: 'PrivacyConsent',
                descriptionHtml: 'Do you agree to the privacy policy?',
                value: 'question-value-3',
                privacyPolicyUrl: {
                  url: 'https://www.example.com',
                },
              },
            }
          ],
        },
      },
    },
  }
);

Step 3: Handle browser token requests

  1. The panel loads and invokes the getAuthToken function passed to it.
  2. Your frontend requests a browser token from your backend.The getAuthToken function should request a new token for the hirer ID in positionProfile.positionOrganizations. If a user switches to a different SEEK hirer account in your posting form, your software should re-render the panel with the new hirer ID in positionProfile.positionOrganizations, and ensure that subsequent invocations of getAuthToken will request a token for the new hirer ID.
  3. Your backend authenticates and authorizes the user.Your software is responsible for verifying that the user is authorized to access a given hirer ID. A user must not be able to request a browser token for an arbitrary organization that they do not belong to.
  4. Your backend requests a browser token from the SEEK API for the appropriate hirer ID and query:enhanced-job-posting, mutation:enhanced-job-posting scope.
  5. Your backend responds with the browser token.
  6. Your frontend returns the browser token from the getAuthToken function.
  7. The panel can now make requests to the GraphQL endpoint.
HTTP
Copy
POST https://graphql.seek.com/auth/token HTTP/2
Authorization: Bearer PARTNER_TOKEN_HERE
Content-Type: application/json
User-Agent: YourPartnerService/1.2.3
{
  "hirerId": "seekAnzPublicTest:organization:seek:93WyyF1h",
  "scope": "query:ad-products query:organizations",
  "userId": "317665"
}