Only posts generated using Odoo are displayed.
- Go to Social Media > Posts
This feature acts as a template for generating multiple posts from a
single view, depending on the selected accounts.
- Go to Social Media > Posts > New or Go to Social Media >
Dashboard > Add Post
- Fill in the required fields
- When the post is created, every X account of the active company is
selected by default in Accounts; review the list and remove the ones
you do not want to use before saving.
- Save
- Click on the Post button
- A publication shares the images and the video of its post, so the
dashboard card shows them, and its video indicator, as soon as X
accepts the message. Nothing waits for an import.
The calls to the X API are paid from the credits bought for the
developer App, see Configuration. The account form shows what is left
of them, so the balance can be followed from Odoo and not only from the
Developer Console.
Go to Social Media > Configuration > Accounts, select the X account
and open the Configuration tab.
Credit Balance (USD) is the total balance X reports for the App,
never negative, with the date it was read next to it. The row stays
hidden until the balance has been read once. The accounts of other
social media do not show it.

The balance belongs to the App that pays the calls, not to the X
account: the accounts registered with the same API Key show the same
figure, and X is asked once per API Key for all of them.
It is read again by the automatic check for updates that runs every 2
hours, when the account is associated, by Update account with no
checkbox selected and by Update statistics of the account form.
If X or the network does not answer, the balance and its date are left
as they were and the reason is only written in the server log: the
date tells how old the figure is. The user is not warned and the
account is not marked as needing an update. When X answers that the
requests for the balance are exhausted, the readings wait for the
window to end, without any notice.
Credit Warning (USD), on the same tab, is the amount under which the
responsible of the account is warned, 1.00 by default. At 0, the
warning is only given when the balance is used up.
The warning is given when a reading finds the balance under the
threshold, once per drop: the following readings under it do not
repeat it, and a reading at or over the threshold arms it again.
Raising the threshold over the current balance counts as a drop, so
the next reading warns.
When the balance reaches 0 the warning says it is used up: X blocks
every call until credits are bought in the Developer Console.
The warning is posted as a note on the chatter of the account that
notifies its responsible, which keeps it when nobody is connected
while the check runs, and it is shown as a notice to the responsible
who is connected, blue when the balance is low and red when it is used
up. When the balance is read while the account is being associated,
the notice of the user who associates it is shown once X sends the
user back to Odoo.
Every account holding the same API Key warns its own responsible.
What X refuses is checked in Odoo before the publication is sent. The
post shows the reason while it is being written, saving is never
blocked, and the publication of the account that raises the objection is
refused instead of being sent and failing on X. The same checks are
applied when the post reaches the publication through an import or an
RPC call, so nothing gets past them.
- The message is checked against the characters the plan of the account
allows: 280 without X Premium and 25 000 with it, see the
creation of a
post. The plan
is the X Premium switch of the account form, read from X, see X
Premium plan of the account above. A longer message is reported on
the post and the publication is not sent. A post selecting several X
accounts is measured against the strictest of them while it is
written, and every publication against its own account when it is
sent, so only the line that cannot publish is refused. An account
whose subscription was cancelled after the plan was last read still
shows X Premium, sends the post and X refuses it: that line is left as
Failed with the reason and a hint to read the plan again with
Update account, see Posts refused by X below.
- X publishes 4 images or 1 video per publication and never both
kinds in the same message, see the media
upload documentation.
A post mixing them is refused instead of being warned about, and so is
one carrying more than four images or more than one video. When the
post carries a video, the preview of X shows the video alone, without
its images, while the form keeps warning that X refuses the post; the
preview of any other social media of the same post is not affected.
- The images are checked against the formats X publishes, JPG, PNG,
WEBP and GIF, and against 5 MB each, 15 MB for a GIF. The
video is checked against MP4 and 512 MB. The message names the
files that cannot be published.
- The file picker is not filtered: it accepts any file of type
image/* or video/*, and the check is what refuses the ones X
does not take.
- The duration of the video is not checked in Odoo: a video X considers
too long is refused by X and the line is left as Failed with the
error it returned.
- A post is refused when it selects two X accounts with the same
username: There are X accounts with the same username (…), please
check to avoid spam errors. X rejects the same content sent twice
from the same account for spam reasons, see the creation of a
post, so the
post is stopped in Odoo instead of failing halfway through.
When X refuses a publication, its line is left as Failed with the
reason, which is also posted on the chatter of the post. The other
accounts of the post are published as usual, and if every account was
refused the post goes back to Draft. What the reason says depends on
the answer of X:
- The developer App cannot spend against the API. X answers
403 Forbidden, either with client-not-enrolled or asking for
an App attached to a Project. The reason asks to connect the App to a
Pay Per Use project in Project Access > Manage, not the Default
project on Standard Basic, and to make sure the account has credit in
the Developer Console, see Configuration. It gives the address of
the X API
pricing page,
written as plain text because the reason of a failed publication is
kept as text.
- A message longer than 280 characters, sent by an account X reported
as X Premium. X answers 400 Bad Request or 403 Forbidden and
the reason is X refused the post of {account}: {answer of X}. What an
account may publish depends on its plan, and X reported X Premium for
this account when it was last read. Press Update account to read the
plan again before trying again. This hint is only given when both
conditions hold: it is how a subscription cancelled after the plan was
last read shows up.
- Any other refusal, a 400 Bad Request or a 403 Forbidden
for the permissions of the App or the content of the post: X refused
the post of {account}: {answer of X}.
The images and the video are uploaded as part of the same publication,
so an upload X refuses is explained the same way. None of these refusals
marks the account as needing an update: the plan, the permissions of the
App or the content of the post are not fixed by authorizing the account
again. Only a 401 Unauthorized is taken as a problem with the
credentials, see X credentials below.
The OAuth
1.0a
tokens of X do not expire, so there is nothing to renew: they only stop
working when the access is revoked from the X application or the keys
are changed. When that happens X answers 401 Unauthorized to the
publication, the reason is kept on the failed publication and the
account is marked as needing an update. Odoo cannot renew it by itself:
associate the account again from Update account. Deleting a
publication from Odoo with such a token does not mark the account: the
deletion is cancelled and only the error returned by X is shown.
Social Media X
This module provides the necessary functionality for basic interaction with the X social media.
Main features:
An X account will never draw a time series in Social Media > Statistics. That screen reads a history per day, and the metrics the API v2 answers are the current lifetime counters of each post, with no way of asking for them grouped by day. Giving X a series means either repeating the lifetime counter on every day, which draws a cumulative curve of a different shape than the other social media on the same graph, or storing the delta between two readings, which is false for any day the scheduled action did not run. Neither is a measurement, so nothing is written: an account of X shows the standard empty view there, and its figures live on its card of the Dashboard, which is where they mean something.
Table of contents
Installation
This module requires the following Python libraries:
You can install them with:
Configuration
To configure this module, you need to:
Please note that you must have a developer account.
Important: the X API v2 endpoints used by this module require a developer App connected to a Pay Per Use project and an account with credit on the pay-per-use pricing of X, see about the X API. There is no Free access tier: an App left on the Default project, whose plan is Standard Basic, cannot spend against the API and is refused, so the account cannot be associated with it. Pay per use is not a subscription — there is no monthly fee and no minimum purchase, the App only needs a positive credit balance — but the balance has to be there before the first call. The association wizard shows this warning, and when X rejects the request for this reason the module replaces the raw error with a message that asks to connect the App to a Pay Per Use project in Project Access > Manage, not the Default project on Standard Basic, to make sure the account has credit in the Developer Console, and that points to the pricing page. Note that such an App can be refused at two different moments: when the request token is asked, before any authorization screen is shown, and the wizard answers with that message; or later, when the module reads the authorized user, and the message is then delivered on the Dashboard.
A call made with an App that cannot spend answers 403 Forbidden, either with "reason": "client-not-enrolled" or asking for an App attached to a Project, since only an enrolled App can belong to one. Every endpoint this module uses is affected: linking the account, refreshing the data of the account, publishing (message and media upload), deleting, checking whether a publication is still online and reading the figures of the recent ones.
What each call of the module costs, according to the pricing of X:
A post that carries a link costs more than ten times a post that does not. X bills the link itself, whether it is tracked or not: social_media_base only routes the links of a publication through link_tracker when it promotes a marketing campaign, and a tracked link is still a link. Whether a post carries one is the single decision that moves the bill of a database that only publishes.
The same resource asked for twice within 24 hours is charged once, which is what keeps the scheduled actions of the module cheap: the check of network updates every two hours and the daily refresh of the figures read the same publications over and over.
So a database that only publishes spends cents a month: this module reads nothing but its own publications, at the price of an owned read, and the credit balance of the App, once per API Key, which is not among the calls the pricing of X lists. What grows with the account is the history, and the history is read by Social Media X Sync, which is a separate module nobody has to install. A deployment that publishes and watches its own figures does not need it.
There is no way around the API for what this module does. Publishing on X through anything else means either a reseller that pays the same API and charges for it on top, or driving the web with the session of the user, which its terms of service forbid and this module does not implement.
Creating the developer account
The developer account is not the X account: it is the enrolment of that X account in the API, and it is asked for once per organization, not per social account published from Odoo.
Sign in at the Developer Console with the X account that owns the App and press Start Building. It does not have to be the account whose posts Odoo publishes — the App belongs to whoever administers it, and each account authorizes it afterwards from the wizard of Social Media X. Accept the Developer Agreement and Policy if X asks for it.
Create a project. The use case declared here is what X reads if it ever reviews the App. A project starts on the default plan of its product, so it can be born on Standard Basic, the plan of the Default project that the console may already show with Plan: Free. Only a project on Pay Per Use spends against the credit balance, so that is the one the App has to be connected to, see Project Access below.
Create the App. The App is the set of keys, and the project is what those keys are allowed to call, so an App connected to no project answers 403 to everything, and so does an App connected only to the Default project on Standard Basic. Do not take for granted which project a new App is connected to: check what its Project Access shows, as described below.
Add a payment method in Billing > Billing information with Add Payment Method, following the steps X asks for.
Buy credits from the Dashboard or from Billing > Credits, before associating any account in Odoo: reading the authorized user is already a billed call, so an App with no balance fails at the association itself and never reaches the Dashboard of Odoo. A spend cap per billing cycle is set on that same screen, and it is what keeps a mistake from spending more than intended — when the cap is reached X blocks the requests until the next cycle.
The screens below are the ones of the developer portal. X moves them from time to time; the names of the settings are what matters, not where the picture puts them.
The steps required for using it are defined below:
Go to the Developer Console
Create a developer account as described above.
Once the account is created, go to Access > Apps and select the App. What it shows as Project Access is the project its keys call through, with its plan in brackets. An App on Default project-… (Standard Basic), as in the picture below, cannot spend against the API and X refuses it.
In that case press Manage on Project Access. The dialog lists the projects of the account, each with its plan and a Connect or Disconnect button: press Connect on the Pay Per Use one. The keys work on every project the App is connected to, and the limits and endpoints follow the plan of that project. The keys do not have to be regenerated: an App connected this way associates with the Consumer Key and Secret it already had. Start a new project, at the bottom of the same dialog, offers the Ads, Enterprise and Community Notes products, none of which is the Pay Per Use project this module needs.
Once connected, the page of the App shows the Pay Per Use project under Project Access.
Open Settings to reach the authentication settings of the App.
Once on the page, in the App Permissions section, select Read and write, which is what the endpoints this module calls need.
Go to the App Type section and select Web App, Automated App or Bot.
Then, in the Callback URI / Redirect URL section, add a new address. Here are the steps to get that URL in Odoo:
Then go to the Website URL section and add your X profile address.
Example: https://x.com/AccountTest
Finally, go to the Keys & Tokens tab of the App, press the Regenerate button of the Consumer Key, and then in the window that appears, confirm the generation of the API Key and API Key Secret values. The Console lists that pair under OAuth 1.0 Keys as the Consumer Key, which is the same thing this module and X itself call API Key and API Key Secret. X shows the pair once, and regenerating it again invalidates the one already stored in Odoo: an App whose keys were regenerated after being registered in Odoo answers {"code": 32, "message": "Could not authenticate you."} to the association.
Learn more at the X Developer Console
Registering the API Key and API Key Secret. Integration of a user account.
Go to Social Media > Configuration > Social Media
Click on the Associate Account button for the desired social media.
A wizard will open for you to add the API Key and API Key Secret obtained from your developer account.
By clicking the Associate button here, you’ll be taken to a X authentication page, which lists what the App is allowed to do with the account. Once you authorize it, you’ll be taken back to the system and the Dashboard view, where you’ll see your posts.
An App that cannot spend against the API is refused here, after the authorization: X answers the reading of the authorized user with 403, the account is not created and the Dashboard explains it. The authorization itself is granted, so nothing has to be undone on the X side. The cause is either the project or the balance, and the Developer Console tells them apart: if the Project Access of the App shows the Default project on Standard Basic, connect the App to the Pay Per Use project as described above; if it already shows a Pay Per Use project, the account has no credit left, so buy credits in Billing > Credits. Then associate again.
Once you have completed these steps and everything is working correctly, you can see your account in Social Media > Configuration > Accounts
Note that the account credentials (API Key, API Secret and tokens) are only visible to users with administration rights (Settings). The account form shows the API Key in read-only mode and the API Secret masked; the tokens are never displayed.
The authorization flow is bound to the session that started it: the request token returned by X only works for the user who opened the wizard. A callback without a request token, or with the request token of another user, is refused without creating or modifying anything; the user gets the generic notice The account could not be associated. Check the server log for details. and the exact reason is kept in the server log, so the raw answer of the provider is never exposed.
Each X account has to be associated with its own pair of credentials: if an account already exists (even archived) with the same API Key and API Secret, the association is refused with An account with that information already exists. Create a different developer App for that account.
Besides the OAuth 1.0a authorization of the user, the module obtains an application-only bearer token (OAuth 2.0 client credentials) from the same API Key and API Secret; that is the one the reads about the posts answer to — the figures of the recent publications and the check that one of them is still online — while reading the authorized user, which is what the association and the update of the data of the account do, travels with the OAuth 1.0a credentials of the user, see about the X API. If X does not deliver it, the account is not created and the notice The account was not created: the OAuth2 access token could not be obtained. is shown. In the same way, if X does not answer the access token correctly when closing the authorization, the account is not created and the user gets the same generic notice, the reason returned by X being kept in the server log.
Publishing with images or video uses the media upload endpoint of the X API v1.1 besides the v2 endpoints. The App has to have access to both: on a plan without that access, the publications with attachments fail even though the text-only ones work.
The X account being authorized has to be active and not protected, and the App needs read and write permission. Odoo does not check it beforehand: if X refuses, the error returned by X is displayed on the Dashboard and the account is not associated.
If the X account is already linked to an account of another Odoo user, the association is refused and nothing is overwritten: only the responsible user of the account and the Social Media / Administrator group can relink it.
Usage
List of posts generated from Odoo.
Only posts generated using Odoo are displayed.
Generate a post.
This feature acts as a template for generating multiple posts from a single view, depending on the selected accounts.
Update token, API Key, API Secret and account data
Go to Social Media > Configuration > Accounts
Select the account
Click on the Update account button
In the wizard that appears, if none of the checkboxes are selected and the Update button is pressed, the system will update only the account’s data, its X Premium plan included, see X Premium plan of the account below, and read the credit balance of the App again, see Credit balance of the App below.
If the Update keys checkbox is selected, the current API Key and API Secret values will be displayed by default. Modify any of these values and authentication will be performed again through X to update these values and the token.
Selecting the Update token checkbox will update the current token.
Associating the account and updating it end with a success notice and stamp the date of Last Update Account on the account form. Associating it, associating it again, Update token and Update keys show The account was associated successfully on the Dashboard once X sends the user back; Update with no checkbox selected shows The account was updated successfully. If X refuses the figures of the account right after associating it, its error is shown after the success notice.
X Premium plan of the account
How long the message of a post may be depends on the plan of the X user who authorized the account: 280 characters without X Premium and 25 000 with it. Odoo reads that plan from X, it is not set by hand.
Go to Social Media > Configuration > Accounts, select the X account and open the Configuration tab. The X Premium switch shows the plan X reported for the account and cannot be changed from the form. It is not carried over when an account is duplicated.
The plan is read from X when the account is associated, when it is associated again from Update account with Update keys or Update token, and by Update account with no checkbox selected. The switch is on when X reports any plan of X Premium, whatever its level (Basic, Premium or Premium+), and off when X reports that the user has no subscription. If X does not report the plan, the switch keeps the value it had.
The automatic check for updates does not read it. A subscription bought or cancelled on X is seen in Odoo after pressing Update account.
X Premium is a subscription of the X user, bought on x.com while signed in with that user, from the Premium entry of its menu. It is not bought in the Developer Console (console.x.com), which belongs to the developer App: its project, its keys, the pay-per-use plan and its credits, see Configuration. The credits of the App do not grant X Premium: an App with credit publishes on an account without the subscription, and that account is still limited to 280 characters.
Credit balance of the App
The calls to the X API are paid from the credits bought for the developer App, see Configuration. The account form shows what is left of them, so the balance can be followed from Odoo and not only from the Developer Console.
Go to Social Media > Configuration > Accounts, select the X account and open the Configuration tab.
Credit Balance (USD) is the total balance X reports for the App, never negative, with the date it was read next to it. The row stays hidden until the balance has been read once. The accounts of other social media do not show it.
The balance belongs to the App that pays the calls, not to the X account: the accounts registered with the same API Key show the same figure, and X is asked once per API Key for all of them.
It is read again by the automatic check for updates that runs every 2 hours, when the account is associated, by Update account with no checkbox selected and by Update statistics of the account form.
If X or the network does not answer, the balance and its date are left as they were and the reason is only written in the server log: the date tells how old the figure is. The user is not warned and the account is not marked as needing an update. When X answers that the requests for the balance are exhausted, the readings wait for the window to end, without any notice.
Credit Warning (USD), on the same tab, is the amount under which the responsible of the account is warned, 1.00 by default. At 0, the warning is only given when the balance is used up.
The warning is given when a reading finds the balance under the threshold, once per drop: the following readings under it do not repeat it, and a reading at or over the threshold arms it again. Raising the threshold over the current balance counts as a drop, so the next reading warns.
When the balance reaches 0 the warning says it is used up: X blocks every call until credits are bought in the Developer Console.
The warning is posted as a note on the chatter of the account that notifies its responsible, which keeps it when nobody is connected while the check runs, and it is shown as a notice to the responsible who is connected, blue when the balance is low and red when it is used up. When the balance is read while the account is being associated, the notice of the user who associates it is shown once X sends the user back to Odoo.
Every account holding the same API Key warns its own responsible.
Archive Account X
X limits and validations
What X refuses is checked in Odoo before the publication is sent. The post shows the reason while it is being written, saving is never blocked, and the publication of the account that raises the objection is refused instead of being sent and failing on X. The same checks are applied when the post reaches the publication through an import or an RPC call, so nothing gets past them.
Posts refused by X
When X refuses a publication, its line is left as Failed with the reason, which is also posted on the chatter of the post. The other accounts of the post are published as usual, and if every account was refused the post goes back to Draft. What the reason says depends on the answer of X:
The images and the video are uploaded as part of the same publication, so an upload X refuses is explained the same way. None of these refusals marks the account as needing an update: the plan, the permissions of the App or the content of the post are not fixed by authorizing the account again. Only a 401 Unauthorized is taken as a problem with the credentials, see X credentials below.
Rate limits
Figures of a publication
Dashboard actions
X credentials
The OAuth 1.0a tokens of X do not expire, so there is nothing to renew: they only stop working when the access is revoked from the X application or the keys are changed. When that happens X answers 401 Unauthorized to the publication, the reason is kept on the failed publication and the account is marked as needing an update. Odoo cannot renew it by itself: associate the account again from Update account. Deleting a publication from Odoo with such a token does not mark the account: the deletion is cancelled and only the error returned by X is shown.
Uninstalling the module
Uninstalling Social Media X does not delete the accounts nor their publication history:
Bug Tracker
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed feedback.
Do not contact contributors directly about support or help with technical issues.
Credits
Authors
Contributors
Other credits
This work has been funded by AEOdoo (Asociación Española de Odoo - https://www.aeodoo.org)
Maintainers
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
Current maintainer:
This module is part of the OCA/social project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.